HTML5ビデオクライアントへのリアルタイムHTTPストリーミングへの最良のアプローチ


212

node.jsを使用してffmpegのリアルタイム出力をHTML5クライアントにストリーミングするための最良の方法を理解しようとするのは本当に大変です。この領域には多くの変数があり、この分野では多くの経験がないためです。さまざまな組み合わせを試すために何時間も費やした。

私のユースケースは:

1)IPビデオカメラRTSP H.264ストリームはFFMPEGによって取得され、ノードの次のFFMPEG設定を使用してmp4コンテナーに再多重化され、STDOUTに出力されます。これは最初のクライアント接続でのみ実行されるため、部分的なコンテンツ要求がFFMPEGを再度生成することはありません。

liveFFMPEG = child_process.spawn("ffmpeg", [
                "-i", "rtsp://admin:12345@192.168.1.234:554" , "-vcodec", "copy", "-f",
                "mp4", "-reset_timestamps", "1", "-movflags", "frag_keyframe+empty_moov", 
                "-"   // output to stdout
                ],  {detached: false});

2)私はノードhttpサーバーを使用してSTDOUTをキャプチャし、クライアントの要求に応じてそれをクライアントにストリーミングします。クライアントが最初に接続するときに、上記のFFMPEGコマンドラインを生成してから、STDOUTストリームをHTTP応答にパイプします。

liveFFMPEG.stdout.pipe(resp);

また、ストリームイベントを使用してFFMPEGデータをHTTP応答に書き込みましたが、違いはありません。

xliveFFMPEG.stdout.on("data",function(data) {
        resp.write(data);
}

次のHTTPヘッダーを使用します(これは、事前に記録されたファイルをストリーミングするときにも使用され、機能します)。

var total = 999999999         // fake a large file
var partialstart = 0
var partialend = total - 1

if (range !== undefined) {
    var parts = range.replace(/bytes=/, "").split("-"); 
    var partialstart = parts[0]; 
    var partialend = parts[1];
} 

var start = parseInt(partialstart, 10); 
var end = partialend ? parseInt(partialend, 10) : total;   // fake a large file if no range reques 

var chunksize = (end-start)+1; 

resp.writeHead(206, {
                  'Transfer-Encoding': 'chunked'
                 , 'Content-Type': 'video/mp4'
                 , 'Content-Length': chunksize // large size to fake a file
                 , 'Accept-Ranges': 'bytes ' + start + "-" + end + "/" + total
});

3)クライアントはHTML5ビデオタグを使用する必要があります。

HTML5クライアントへのストリーミング再生(fs.createReadStreamを206 HTTP部分コンテンツを使用)で問題がなく、上記のFFMPEGコマンドラインで以前に記録されたビデオファイル(ただし、STDOUTではなくファイルに保存されます)なので、FFMPEGストリームを知っています。正しく、HTTPノードサーバーに接続すると、VLCでビデオのライブストリーミングを正しく表示することもできます。

ただし、ノードHTTPを介してFFMPEGからライブストリームを送信しようとすると、クライアントが1つのフレームを表示して停止するため、はるかに困難なようです。問題は、HTML5ビデオクライアントと互換性があるようにHTTP接続を設定していないことだと思います。HTTP 206(部分的なコンテンツ)と200応答を使用してデータをバッファに入れ、その後運が悪い状態でストリーミングするなど、さまざまなことを試しました。これを正しく設定するには、最初の原則に戻る必要があります。仕方。

これがどのように機能するかについての私の理解はここにあります、私が間違っているなら私を修正してください:

1)出力をフラグメント化し、空のmoov(FFMPEG frag_keyframeおよびempty_moov movフラグ)を使用するようにFFMPEGを設定する必要があります。これは、クライアントが、通常はファイルの最後にあるmoovアトムを使用しないことを意味します。これは、ストリーミング時に関係ない(ファイルの最後ではない)が、シークが不可能であることを意味します。

2)MP4フラグメントと空のMOOVを使用している場合でも、HTML5プレーヤーはストリーム全体がダウンロードされるまで待機してから再生するため、ライブストリームでは終了せず、機能しません。

3)ライブストリーミング中にSTDOUTストリームをHTTP応答にパイプすることがなぜ機能しないのかがわかりません。ファイルに保存した場合、同様のコードを使用してこのファイルをHTML5クライアントに簡単にストリーミングできます。FFMPEGスポーンの開始、IPカメラへの接続、およびチャンクのノードへの送信に1秒かかるため、タイミングの問題である可能性があります。また、ノードデータイベントも不規則です。ただし、バイトストリームはファイルへの保存とまったく同じである必要があり、HTTPは遅延に対応できる必要があります。

4)FFMPEGによって作成されたMP4ファイルをカメラからストリーミングするときにHTTPクライアントからネットワークログを確認すると、3つのクライアントリクエストがあることがわかります。ビデオの一般的なGETリクエストで、HTTPサーバーは約40Kbを返し、次にパーシャルファイルの最後の10Kのバイト範囲を含むコンテンツ要求、次にロードされていない中央のビットの最終要求。HTML5クライアントは、最初の応答を受け取った後、ファイルの最後の部分でMP4 MOOVアトムをロードするように求めているのでしょうか。これが当てはまる場合、MOOVファイルがなく、ファイルの終わりがないため、ストリーミングでは機能しません。

5)ライブストリーミングを試行しているときにネットワークログを確認すると、約200バイトしか受信しないで中止された最初のリクエストが表示され、その後、再度リクエストが200バイトで中断され、3番目のリクエストが2Kの長さしかありません。記録されたファイルからストリーミングするときにバイトストリームが正常に使用できるものとまったく同じであるため、HTML5クライアントがリクエストを中止する理由がわかりません。また、ノードがFFMPEGストリームの残りをクライアントに送信していないようですが、FFMPEGデータを.onイベントルーチンで確認できるため、FFMPEGノードのHTTPサーバーに到達しています。

6)STDOUTストリームをHTTP応答バッファーにパイプすることは機能すると思いますが、中間バッファーおよびストリームを構築する必要がありますか? ?これが私の問題の主な理由だと思いますが、Nodeでそれを最適に設定する方法は正確にはわかりません。また、ファイルの終わりがないため、ファイルの終わりにあるデータのクライアント要求を処理する方法がわかりません。

7)206の部分的なコンテンツ要求を処理しようとして、私は間違った軌道に乗っていますか?これは通常の200 HTTP応答で機能する必要がありますか?HTTP 200応答はVLCで正常に機能するので、HTML5ビデオクライアントは部分的なコンテンツ要求でのみ機能するのではないでしょうか。

私はまだこのことを学んでいるので、この問題のさまざまなレイヤー(FFMPEG、ノード、ストリーミング、HTTP、HTML5ビデオ)を処理するのは難しいので、どんなポインターでも大歓迎です。私はこのサイトとネットの調査に何時間も費やしており、ノードでリアルタイムストリーミングを行うことができた人に出会ったことはありませんが、私は最初にすることはできません。これはうまくいくはずです(どういうわけか) !)。


4
これはトリッキーなテーマです。まず最初に。Content-Typeあなたは頭を抱えましたか?チャンクエンコーディングを使用していますか?それが私が始めるところです。また、HTML5はストリーミングする機能を必ずしも提供するわけではありません。詳しくはこちらご覧ください。これは十分にサポートされていない可能性が高いと思われますが、おそらく、独自の手段を使用してビデオストリームをバッファリングして再生する方法を実装する必要があります(こちらを参照)。MediaSource APIにググる。
tsturzl 2014

返信いただきありがとうございます。はい、content-typeは 'video / mp4'で、このコードはストリーミングビデオファイルで機能します。残念ながらMediaSourceはChromeのみなので、他のブラウザーをサポートする必要があります。HTML5ビデオクライアントがHTTPストリーミングサーバーと対話する方法に関する仕様はありますか?私が何をしたいかは確かですが、正確な方法がわかりません(node.jsを使用しますが、C#またはC ++を使用する方が簡単です)
deandob

2
バックエンドに問題はありません。あなたはうまくビデオをストリーミングしています。問題はフロントエンド/クライアントにあり、自分でストリーミングを実装する必要があります。HTML5は単にストリームを処理しません。ほとんどの場合、ブラウザーごとにオプションを検討する必要があります。ビデオタグとメディアAPIのw3標準を読むことから始めるのが良いでしょう。
tsturzl 2014

この仕事をすることは可能であるべきだと思われます。私は明確な答えを提供していませんが、この問題はブラウザがビデオストリームの次のフレームではなく、最初にmp4コンテナのヘッダー/アトムの残りを期待しているという事実に関連していると思います。非常に長いビデオのMOOVアトム(プレーヤーが要求し続けるように)とその他の予期されるヘッダーを送信し、ffmpegからコピーを開始した場合、これは機能する可能性があります。また、ブラウザーでjsを使用してスクラブバーを非表示にし、前方にスキャンできないようにする必要もあります。
jwriteclub

WebRTCを検討することをお勧めします。WebRTCは、日々より良いクロスブラウザーサポートを獲得しています。
Alex Cohn、2014

回答:


209

編集3:IOS 10以降、HLSは断片化されたmp4ファイルをサポートします。ここでの答えは、DASHおよびHLSマニフェストを使用して、フラグメント化されたmp4アセットを作成することです。>ふりフラッシュ、iOS9以下、IE 10以下は存在しません。

この行の下にあるものはすべて古くなっています。後世のためにここに保管します。


編集2:コメントの人々が指摘しているように、物事は変化します。ほとんどすべてのブラウザがAVC / AACコーデックをサポートします。iOSには引き続きHLSが必要です。ただし、hls.jsなどのアダプターを使用すると、MSEでHLSを再生できます。iOSが必要な場合、新しい答えはHLS + hls.jsです。または、断片化されたMP4(つまりDASH)を使用しない場合

ビデオ、特にライブビデオが非常に難しいのには、多くの理由があります。(元の質問ではHTML5ビデオが要件であると明記されていましたが、質問者はコメントでFlashが可能であると述べました。そのため、この質問はすぐに誤解を招きます)

まず言い直します。HTML5を介したLIVE STREAMINGの公式サポートはありません。ハックはありますが、走行距離は異なる場合があります。

編集:私がこの回答を書いて以来、Media Source Extensionsは成熟し、現在は実行可能なオプションに非常に近くなっています。それらはほとんどの主要なブラウザでサポートされています。IOSは引き続きホールドアウトです。

次に、ビデオオンデマンド(VOD)とライブビデオは大きく異なることを理解する必要があります。はい、どちらもビデオですが、問題が異なるため、形式が異なります。たとえば、コンピューターのクロックが本来よりも1%速く実行されている場合、VODに気付かないでしょう。ライブビデオでは、それが発生する前にビデオを再生しようとします。進行中のライブビデオストリームに参加する場合は、デコーダーを初期化するために必要なデータが必要なので、ストリーム内で繰り返すか、帯域外に送信する必要があります。VODを使用すると、ファイルの先頭を読み取り、ユーザーが希望する任意のポイントにシークできます。

少し掘り下げましょう。

プラットフォーム:

  • iOS
  • パソコン
  • マック
  • アンドロイド

コーデック:

  • vp8 / 9
  • h.264
  • thora(vp3)

ブラウザでのライブビデオの一般的な配信方法:

  • ダッシュ(HTTP)
  • HLS(HTTP)
  • フラッシュ(RTMP)
  • フラッシュ(HDS)

ブラウザでのVODの一般的な配信方法:

  • DASH(HTTPストリーミング)
  • HLS(HTTPストリーミング)
  • フラッシュ(RTMP)
  • フラッシュ(HTTPストリーミング)
  • MP4(HTTP疑似ストリーミング)
  • MKVとOOGについては、あまり詳しくないので説明しません。

html5ビデオタグ:

  • MP4
  • webm
  • オッグ

どのブラウザがどのフォーマットをサポートしているか見てみましょう

サファリ:

  • HLS(iOSおよびMacのみ)
  • h.264
  • MP4

Firefox

  • DASH(MSE経由、ただしh.264経由なし)
  • Flash経由のh.264のみ!
  • VP9
  • MP4
  • OGG
  • Webm

IE

  • 閃光
  • DASH(MSE IE 11以降のみ)
  • h.264
  • MP4

クロム

  • 閃光
  • DASH(MSE経由)
  • h.264
  • VP9
  • MP4
  • webm
  • オッグ

MP4はライブビデオには使用できません(注:DASHはMP4のスーパーセットなので、混同しないでください)。MP4はmoovとmdatの2つの部分に分かれています。mdatには、生のオーディオビデオデータが含まれています。ただし、索引付けされていないため、moovがなければ役に立たない。moovには、mdat内のすべてのデータのインデックスが含まれています。ただし、そのフォーマットのため、タイムスタンプとすべてのフレームのサイズがわかるまで、「フラット化」することはできません。フレームサイズを「変更」するmoovを作成することは可能ですが、帯域幅に関しては非常に無駄です。

したがって、どこにでも配信したい場合は、最も一般的な分母を見つける必要があります。フラッシュの例に頼らないと、ここにLCDがないことがわかります。

  • iOSはh.264ビデオのみをサポートします。ライブのHLSのみをサポートします。
  • Firefoxは、フラッシュを使用しない限り、h.264をまったくサポートしていません。
  • iOSでFlashが機能しない

LCDに最も近いのは、HLSを使用してiOSユーザーを獲得し、他の人のためにフラッシュすることです。私の個人的なお気に入りは、HLSをエンコードし、フラッシュを使用して他のすべての人のためにHLSを再生することです。JWプレーヤー6を介してHLSをフラッシュで再生できます(または、私のようにAS3でFLVに独自のHLSを書き込みます)。

間もなく、これを行う最も一般的な方法は、iOS / Mac上のHLSと他のあらゆる場所のMSEを介したDASHになります(これは、Netflixが間もなく実現する予定です)。しかし、私たちはまだ誰もがブラウザをアップグレードするのを待っています。また、Firefox用に別のDASH / VP9が必要になる可能性もあります(open264については知っていますが、これは問題です。メインプロファイルまたはハイプロファイルでビデオを再生することはできません。そのため、現在は役に立ちません)。


さまざまなオプションの詳細な背景と賛否両論をありがとうございました。概念の概要は、元の質問に回答するために見つけた特定の修正よりも重要であるため、私はこの回答を受け入れられたものとして選択しました。賞金で頑張ってください!
deandob 2014年

9
これはこの質問に対する有効な解決策ではありません。この問題の解決策は以下にあります。
jwriteclub

2
FirefoxはMSEおよびh.264をネイティブでサポートするようになりました。最新のFFブラウザーでwww.youtube.com/html5にアクセスして確認します。私はFF 37でテストしました。MacのSafari 8+はMSEもサポートするようになりました。
BigTundra

@BigTundraはい、MacでYosemiteを起動して以来、safariはMSEをサポートしています。しかし、iOSではありません。Windowsについてはわかりません。(Windowsのサファリはまだ存在しますか?)(私の)Mac上のFirefox 37.0.2は、そのリンクによるとMSEをまったくサポートしていないようです。ただし、H.264はサポートしています。Firefoxは過去にH.264サポートを追加、削除、再追加しました。
2015年

MPEG-4 / H.264ビデオ形式の最新のブラウザーサポート:caniuse.com/#feat=mpeg4
Maxence

75

これは複雑な質問であり、ライブビデオをストリーミングする前に機能する必要のある多くのレイヤーがあるため、特にszatmaryに感謝します。私の元の質問とHTML5ビデオの使用とフラッシュを明確にするために-私のユースケースはHTML5を強く好んでいます。なぜなら、それは一般的であり、クライアントと将来に簡単に実装できるからです。Flashは2番目に優れているので、この質問にはHTML5を使いましょう。

この演習を通じて多くのことを学び、ライブストリーミングはVOD(HTML5ビデオでうまく機能します)よりもはるかに難しいことに同意します。しかし、私はこれを私のユースケースで十分に機能させ、MSE、フラッシュ、精巧なバッファリングスキームなどのより複雑なオプションをノードで追跡した結果、ソリューションは非常にシンプルであることがわかりました。問題は、FFMPEGがフラグメント化されたMP4を破損していて、FFMPEGパラメーターを調整する必要があり、私が最初に使用したhttp経由の標準ノードストリームパイプのリダイレクトだけで十分だったということです。

MP4には、独自のインデックスを持つ非常に小さなフラグメントにmp4を分割し、mp4ライブストリーミングオプションを実行可能にする「フラグメンテーション」オプションがあります。ただし、ストリームにシークすることはできません(私の使用例ではOK)。それ以降のバージョンのFFMPEGはフラグメンテーションをサポートしています。

タイミングが問題になる可能性があることに注意してください。私の解決策では、再多重化の組み合わせによって2〜6秒の遅延が生じます(実質的にFFMPEGはライブストリームを受信し、再多重化してからHTTP経由で提供するためにノードに送信する必要があります) 。これについてはそれほど多くのことはできませんが、Chromeでは、ビデオはできる限り追いつくように試みます。これにより、ビデオは少しびくびくしますが、IE11(私の優先クライアント)よりも最新の状態になります。

この投稿でコードがどのように機能するかを説明するのではなく、コメント付きのGISTを確認してください(クライアントコードは含まれていません。これは、ノードのhttpサーバーアドレスを持つ標準のHTML5ビデオタグです)。GISTはこちら:https : //gist.github.com/deandob/9240090

私はこの使用例の同様の例を見つけることができなかったので、上記の説明とコードが他の人に役立つことを願っています。

これが私の特定の質問に対する回答ですが、szatmaryの回答が最も包括的であるため、受け入れられたものとして選択しました。


33
申し訳ありませんが、私はこれを自分で見つけました。私の回答を書いてみると、これはかなりはっきりしています。以前の回答はすべて役に立ち、高く評価されましたが、大きな貢献はありませんでした。私はGISTで作業コードを提出しましたが、他の誰も提出していません。私は「評判」には興味がありません。自分のアプローチとコードを改善できるかどうかを知ることに興味があります。そして、私がチェックした答えは私の問題を解決しました、それで私は問題がここに何があるのか​​混乱しています。私はSOにかなり慣れていないので、別の方法で対話するように言われてうれしいです。このサイトは有用だと思います。私の答えは他の人を助けるはずです。
deandob 2014年

2
このコミュニティーでは、元の問題が解決されたとしても、質問した場合に受け入れられた回答としてその回答を選択することは適切ではないようです。直感に反するように見えますが、概念のドキュメントは実際の修正よりも重要です。他の人が学ぶのに役立つので、問題はありません。私は自分の答えの選択を解除し、コンセプトの最も明確なものとしてszatmaryを選択しました。
deandob 2014年

6
@deandob:私はあなたがうまく提供したこの問題の実用的な解決策に対する報奨金を投稿しました。受け入れられた回答は、有効な解決策がないため、明らかに不正確であると主張しています。
jwriteclub

2
ありがとう。他の人が私の元の答えを間違っていると反対票を投じたようであり、私は新しいので、私はそれがここでの回避策だと思いました。騒ぎたくはありませんが、メタスタックオーバーフローの関係者に確認します。ところで-私のソリューションは非常にうまく機能しており、他の人にとっても機能するはずです。投稿されたソリューションには、最初のラグを減らすことができるバリエーションがあります(node.jsのバッファーは、最初にクライアントエンドでストリームの終わりを探します)。 。
deandob 2014年

4
モデレーターから、質問に自分で答え、それを答えとして選択するという私の元のアプローチが正しいアプローチであったことを明確にしました。詳細(またはこれについてさらに議論したい場合)については、メタサイトのスレッドを参照してください。meta.stackexchange.com/questions/224068/...
deandob

14

JSMPEGプロジェクトを見てください。そこで実装されている素晴らしいアイデアがあります— JavaScriptを使用してブラウザでMPEGをデコードすることです。エンコーダー(FFMPEGなど)からのバイトは、WebSocketまたはFlashなどを使用してブラウザーに転送できます。コミュニティが追いつくとしたら、それは今のところ最高のHTML5ライブビデオストリーミングソリューションになると思います。


10
これはMPEG-1ビデオデコーダです。MPEG-1の古さを理解しているかはわかりません。DVDより古いです。それはだ少し GIFファイルよりも、より高度な。
Camilo Martin

13

ブロードウェイのh264コーデック(emscripten)を中心に、すべてのブラウザー(デスクトップ、iOSなど)でライブ(遅延なし)h264ビデオを再生できるHTML5ビデオプレーヤーを作成しました。

ビデオストリームは、websocketを介してクライアントに送信され、フレームごとにデコードされ、canvaに表示されます(加速にはwebglを使用)

githubのhttps://github.com/131/h264-live-playerをチェックしてください


1
github.com/Streamedian/html5_rtsp_playerこれらの人たちは、websocketでrtp h264を使用する同様のことをしました
Victor.dMdB

12

RTSPベースのWebカメラをHTML5クライアントにライブストリーミングする1つの方法(再エンコードが含まれるため、品質の低下が予想され、CPUパワーが必要です):

  • アイスキャストサーバーをセットアップします(ウェブサーバーと同じマシン上、またはカムからRTSPストリームを受信するマシン上にある可能性があります)
  • カメラからストリームを受信するマシンでは、FFMPEGではなくgstreamerを使用します。RTSPストリームを受信して​​デコードし、再エンコードして、icecastサーバーにストリーミングすることができます。パイプラインの例(ビデオのみ、オーディオなし):

    gst-launch-1.0 rtspsrc location=rtsp://192.168.1.234:554 user-id=admin user-pw=123456 ! rtph264depay ! avdec_h264 ! vp8enc threads=2 deadline=10000 ! webmmux streamable=true ! shout2send password=pass ip=<IP_OF_ICECAST_SERVER> port=12000 mount=cam.webm

=>次に、<video>タグをicecast-streamのURL(http://127.0.0.1:12000/cam.webm)で使用すると、webmをサポートするすべてのブラウザーとデバイスで機能します


3

このソリューションを見てください。私が知っているように、Flashphonerは純粋なHTML5ページでライブオーディオ+ビデオストリームを再生できます。

再生にはMPEG1およびG.711コーデックを使用します。ハックは、デコードされたビデオをHTML5キャンバス要素にレンダリングし、HTML5オーディオコンテキストを介してデコードされたオーディオを再生します。



2

これはよくある誤解です。HTML5ビデオのライブサポートはありません(iOSおよびMac Safari上のHLSを除く)。webmコンテナーを使用してそれを「ハッキング」できるかもしれませんが、それが普遍的にサポートされるとは思いません。探しているものはMedia Source Extensionsに含まれており、フラグメントを一度に1つずつブラウザーにフィードできます。ただし、クライアント側のJavaScriptを記述する必要があります。


ありますsolutionssupport、ライブストリーミングにはありません。これは、上記の私のコメントを直接参照しています。また、webmは主要なブラウザーでサポートされており、ほとんどが最新の安定バージョンです。
tsturzl 2014

1
H.264からwebmにトランスコードしない方がいいので、必要ありません。また、IE11とSafariをサポートする必要があるため、MediaSource拡張機能は役に立ちません。しかし、サーバーサイドでファイルストリームをシミュレートすると(機能します!)機能するはずですが、node.jsでファイルバッファーをシミュレートする必要があります。
deandob 2014

1
他の提案として、VLCやフラッシュプラグインとは異なり、ネイティブなWebRTCを使用する可能性を模索します。この技術はまだ実装が難しいことを知っています。幸運を。

1
フラグメントモードを使用するとmp4に破損があったように見えるため、これをFFMPEGの最新バージョンに更新することでこれを機能させました(MP4ライブストリーミングに必要なため、クライアントは、ライブ時に表示されないmoovインデックスファイルを待機していません。ストリーミング)。そして、FFMPEGストリームを直接ブラウザーにリダイレクトする私のnode.jsコードが機能するようになりました。
deandob 2014

1
はい、IE11(私の優先ブラウザー)では問題なく動作します。Chromeでびくびく応答します。
deandob 2014年

2

binaryjsを試してください。これはsocket.ioと同じですが、オーディオビデオをストリーミングするという点だけが優れています。Binaryjs google it


1
Binary.JSはSocket.IOに似ています。また、メディアストリーミングに固有のものではありません。
ブラッド
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.