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ビデオ)を処理するのは難しいので、どんなポインターでも大歓迎です。私はこのサイトとネットの調査に何時間も費やしており、ノードでリアルタイムストリーミングを行うことができた人に出会ったことはありませんが、私は最初にすることはできません。これはうまくいくはずです(どういうわけか) !)。
Content-Type
あなたは頭を抱えましたか?チャンクエンコーディングを使用していますか?それが私が始めるところです。また、HTML5はストリーミングする機能を必ずしも提供するわけではありません。詳しくはこちらをご覧ください。これは十分にサポートされていない可能性が高いと思われますが、おそらく、独自の手段を使用してビデオストリームをバッファリングして再生する方法を実装する必要があります(こちらを参照)。MediaSource APIにググる。