web-dev-qa-db-ja.com

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

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

私のユースケースは:

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

liveFFMPEG = child_process.spawn("ffmpeg", [
                "-i", "rtsp://admin:[email protected]: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ビデオタグを使用する必要があります。

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

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

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

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

2)MP4フラグメントと空のMOOVを使用したとしても、再生前にストリーム全体がダウンロードされるまでHTML 5プレーヤーが待機するため、HTTP部分コンテンツを使用する必要があります。

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

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

5)ライブストリーミングを試みるときにネットワークログをチェックすると、受信された約200バイトで最初のリクエストが打ち切られ、次に200バイトで2回だけのリクエストが再度打ち切られます。バイトストリームは、記録されたファイルからストリーミングするときに使用できるものとまったく同じなので、HTML5クライアントが要求を中止する理由はわかりません。また、nodeがFFMPEGストリームの残りをクライアントに送信していないように見えますが、.onイベントルーチンでFFMPEGデータを確認できるため、FFMPEGノードのHTTPサーバーに到達しています。

6)STDOUTストリームをHTTP応答バッファにパイプすることはうまくいくはずだと思いますが、HTTP部分コンテンツクライアントの要求が(正常に)ファイルを読んだときのように正しく動作できるように中間バッファとストリームを構築する必要がありますか? ?これが私の問題の主な理由であると思いますが、Nodeでそれをどのように設定するのが正確かはわかりません。ファイルの終わりがないため、ファイルの終わりでデータに対するクライアント要求を処理する方法がわかりません。

7)206個の部分的なコンテンツ要求を処理しようとしているとき、私は間違った方向に進んでいますか?そしてこれは通常の200のHTTP応答で動作するはずですか? HTTP 200応答はVLCにはうまく機能するので、HTML 5ビデオクライアントは部分的なコンテンツ要求でのみ機能すると思われますか。

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

198
deandob

編集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が可能であると述べました。そのため、すぐに、この質問は誤解を招きます)

THERE IS HTML5を介したライブストリーミングの公式サポートはありません 。ハックがありますが、あなたの走行距離は変わるかもしれません。

編集:私はこの答えを書いて以来、メディアソースエクステンションは成熟しており、そして現在実行可能な選択肢になることに非常に近いです。それらはほとんどの主要なブラウザでサポートされています。 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
  • ウェブ
  • 卵子

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

サファリ:

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

Firefox

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

IE

  • Flash
  • ダッシュ(MSE経由[IE 11+のみ)
  • h.264
  • MP4

クロム

  • Flash
  • ダッシュ(MSE経由)
  • h.264
  • VP9
  • MP4
  • ウェブ
  • 卵子

MP4はライブビデオには使用できません(注:DASHはMP4のスーパーセットです。混同しないでください)。 MP4は、moovとmdatの2つの部分に分けられます。 mdatは生のオーディオビデオデータを含みます。しかしそれは索​​引付けされていないので、moovがなければ意味がありません。 moovは、mdat内の全データのインデックスを含みます。しかし、そのフォーマットのために、タイムスタンプとEVERYフレームのサイズがわかるまで、フラット化することはできません。フレームサイズを「固定」する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について知っています;それは吸います。メインまたはハイプロファイルでビデオを行うことはできません。したがって、現在は無用です)。

204
szatmary

これは複雑な質問であり、多くのレイヤーがあり、ライブビデオをストリーミングする前に機能している必要があります。私の最初の質問とHTML 5ビデオの使用とフラッシュの関係を明確にするために - 私の使用事例はHTML 5が強く好まれています。 Flashは遠い二番目に良いので、この質問にはHTML5を使い続けましょう。

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

MP4にはそれ自身のインデックスを持っていてmp4ライブストリーミングオプションを実行可能にするはるかに小さい断片にmp4を分割する「断片化」オプションがあります。しかし、ストリームに戻ることはできません(私のユースケースでは問題ありません)。それ以降のバージョンのFFMPEGでは、フラグメンテーションがサポートされています。

タイミングが問題になる可能性があることに注意してください、そして私の解決策で私はリマックスの組み合わせによって引き起こされる2から6秒の遅れを持っています(事実上FFMPEGはライブストリームを受信し、HTTP経由でサービスを提供するために再送信します) 。これについてはそれほど多くはできませんが、Chromeではビデオができる限り追いつこうとしますが、IE 11(私の好みのクライアント)よりも少し動きがぎこちなくなります。

この記事でコードがどのように機能するかを説明するのではなく、コメント付きで要旨をチェックしてください(クライアントコードは含まれていません。ノードhttpサーバーアドレスを持つ標準のHTML5ビデオタグです)。要旨はここにあります: https://Gist.github.com/deandob/9240090

私はこのユースケースの似たような例を見つけることができなかったので、上記の説明とコードが他の人に役立つことを願っています。

これが私の具体的な質問に対する答えですが、私はszatmaryの答えを最も包括的なものとして受け入れたものとして選択しました。

74
deandob

_ jsmpeg _ projectを見てください。 JavaScriptを使用してブラウザでMPEGをデコードするという、そこに実装されている素晴らしいアイデアがあります。エンコーダからのバイト数(たとえばFFMPEG)は、たとえばWebSocketsまたはFlashを使用してブラウザに転送できます。コミュニティが追いつく場合、私はそれが今のところ最高のHTML5ライブビデオストリーミングソリューションになると思います。

13

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

ビデオストリームはWebSocketを介してクライアントに送信され、1フレームごとにデコードされたキャンバスに表示されます(アクセラレーションにはWebglを使用)。

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

12
131

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

  • Icecastサーバーをセットアップします(Webサーバーがあるのと同じマシン、またはカムから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
    

=>あなたはそれからicecast-streamのURLで<video>タグを使うことができます( http://127.0.0.1:12000/cam.webm )そしてそれはwebmをサポートするあらゆるブラウザとデバイスで動作するでしょう

11
Jannis

どうやってjpegソリューションを使うのですか?サーバにjpegを一つずつブラウザに配布させてから、canvas要素を使ってこれらのjpegを描くだけですか? http://thejackalofjavascript.com/rpi-live-streaming/ /

3
Kiki.J.Hu

この解決策 を見てください。私が知っているように、Flashphonerは純粋なHTML5ページでライブオーディオ+ビデオストリームを再生することを可能にします。

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

3
ankitr

Binaryjsを試してください。 socket.ioに似ていますが、うまくいっているのはオーディオビデオをストリーミングすることだけです。 Binaryjs google it

2
Siddharth

これは非常に一般的な誤解です。 HTML5ビデオのライブサポートはありません(iOSおよびMac Safari上のHLSを除く)。あなたはwebmコンテナを使ってそれを 'ハック'することができるかもしれませんが、私はそれが普遍的にサポートされることを期待しません。あなたが探しているものは、あなたが一度に一つずつブラウザに断片を供給することができるMedia Source Extensionsに含まれています。しかし、クライアントサイドのJavaScriptを書く必要があります。

2
szatmary