node.jsを使用してffmpegのリアルタイム出力をHTML5クライアントにストリーミングするための最良の方法を理解しようとすると、本当に行き詰まります。さまざまな変数が使用されており、この分野での経験があまりないためです。さまざまな組み合わせを試すのに何時間も費やした。
私のユースケースは次のとおりです。
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を使用)することに問題はないので、FFMPEGストリームを知っていますは正しく、HTTPノードサーバーに接続すると、VLCでビデオライブストリーミングを正しく表示することもできます。
ただし、クライアントが1つのフレームを表示してから停止するため、ノードHTTPを介してFFMPEGからライブストリーミングを試みるのは非常に難しいようです。問題は、HTML5ビデオクライアントと互換性があるようにHTTP接続を設定していないことだと思います。HTTP 206(部分コンテンツ)と200の応答を使用して、データをバッファーに入れてからストリーミングするなど、さまざまなことを試しましたが、運が悪かったので、最初の原則に戻って、これが正しく設定されていることを確認する必要があります仕方。
これがどのように機能するかについての私の理解です。間違っている場合は訂正してください。
1)FFMPEGは、出力をフラグメント化し、空のmoov(FFMPEGfrag_keyframeおよびempty_moovmovフラグ)を使用するように設定する必要があります。これは、クライアントが、ストリーミング時に関係のない(ファイルの終わりがない)通常はファイルの終わりにあるmoovアトムを使用しないことを意味しますが、私のユースケースには問題ないシークが不可能であることを意味します。
2)MP4フラグメントと空のMOOVを使用していますが、HTML5プレーヤーはストリーム全体がダウンロードされるまで待機してから再生するため、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バイトのリクエストと2Kの長さの3番目のリクエストが再度中止されました。バイトストリームは、記録されたファイルからストリーミングするときに正常に使用できるものとまったく同じであるため、HTML5クライアントがリクエストを中止する理由がわかりません。また、ノードが残りのFFMPEGストリームをクライアントに送信していないようですが、.onイベントルーチンでFFMPEGデータを確認できるため、FFMPEGノードのHTTPサーバーに到達しています。
6)STDOUTストリームをHTTP応答バッファーにパイプすることは機能するはずですが、HTTP部分コンテンツクライアント要求が(正常に)ファイルを読み取るときと同じように適切に機能できるようにする中間バッファーとストリームを構築する必要がありますか? ?これが私の問題の主な理由だと思いますが、ノードでそれを最適に設定する方法が正確にはわかりません。また、ファイルの終わりがないため、ファイルの終わりにあるデータに対するクライアント要求を処理する方法がわかりません。
7)206の部分的なコンテンツ要求を処理しようとして間違った方向に進んでいますか?これは通常の200のHTTP応答で機能する必要がありますか?HTTP 200応答はVLCで正常に機能するので、HTML5ビデオクライアントは部分的なコンテンツ要求でのみ機能すると思いますか?
私はまだこのことを学んでいるので、この問題のさまざまなレイヤー(FFMPEG、ノード、ストリーミング、HTTP、HTML5ビデオ)を処理するのは難しいので、ポインターをいただければ幸いです。私はこのサイトとネットで何時間も研究してきましたが、ノードでリアルタイムストリーミングを行うことができた人は誰もいませんが、私は最初ではありません。これはうまくいくはずです(どういうわけか) !)。
編集3:IOS 10以降、HLSは断片化されたmp4ファイルをサポートします。今の答えは、DASHとHLSマニフェストを使用して断片化されたmp4アセットを作成することです。>フラッシュのふり、iOS9以下およびIE10以下は存在しません。
編集2:コメントの人々が指摘しているように、物事は変化します。ほとんどすべてのブラウザがAVC / AACコーデックをサポートします。iOSにはまだHLSが必要です。ただし、hls.jsなどのアダプターを介して、MSEでHLSを再生できます。iOSが必要な場合、新しい答えはHLS + hls.jsです。または、断片化されたMP4(つまり、DASH)を使用しない場合は、
ビデオ、特にライブビデオが非常に難しい理由はたくさんあります。(元の質問ではHTML5ビデオが必須であると指定されていましたが、質問者はコメントでFlashが可能であると述べていました。したがって、すぐにこの質問は誤解を招く可能性があります)
最初に言い直します。HTML5を介したライブストリーミングの公式サポートはありません。ハックはありますが、マイレージは異なる場合があります。
編集:私がこの回答を書いたので、Media Source Extensionsは成熟し、今では実行可能なオプションに非常に近づいています。それらはほとんどの主要なブラウザでサポートされています。IOSは引き続き頑張っています。
次に、ビデオオンデマンド(VOD)とライブビデオが大きく異なることを理解する必要があります。はい、どちらもビデオですが、問題が異なるため、フォーマットが異なります。たとえば、コンピューターの時計が本来より1%速く実行されている場合、VODに気付くことはありません。ライブビデオでは、発生する前にビデオを再生しようとします。進行中のライブビデオストリームに参加する場合は、デコーダーを初期化するために必要なデータが必要なので、ストリーム内で繰り返すか、帯域外で送信する必要があります。VODを使用すると、ファイルの先頭を任意の場所まで読み取ることができます。
それでは、少し掘り下げてみましょう。
プラットフォーム:
コーデック:
ブラウザでのライブビデオの一般的な配信方法:
ブラウザでのVODの一般的な配信方法:
html5ビデオタグ:
どのブラウザがどのフォーマットをサポートしているかを見てみましょう
サファリ:
Firefox
IE
クロム
MP4はライブビデオには使用できません(注:DASHはMP4のスーパーセットであるため、混同しないでください)。MP4は、moovとmdatの2つの部分に分かれています。mdatには、生のオーディオビデオデータが含まれています。ただし、インデックスが作成されていないため、moovがないと役に立ちません。moovには、mdat内のすべてのデータのインデックスが含まれています。ただし、その形式のため、すべてのフレームのタイムスタンプとサイズがわかるまで「フラット化」することはできません。フレームサイズを「フィブ」するムーブを構築することは可能かもしれませんが、帯域幅に関しては非常に無駄です。
したがって、どこにでも配信したい場合は、最小公倍数を見つける必要があります。フラッシュの例に頼らなければ、ここにはLCDがないことがわかります。
LCDに最も近いのは、HLSを使用してiOSユーザーを獲得し、他のすべての人のためにフラッシュすることです。私の個人的なお気に入りは、HLSをエンコードしてから、フラッシュを使用して他のすべての人のためにHLSを再生することです。JWプレーヤー6を介してフラッシュでHLSを再生できます(または、私が行ったように、AS3で独自のHLSをFLVに書き込むことができます)
間もなく、これを行う最も一般的な方法は、iOS / MacでのHLSと、他のすべての場所でのMSEを介したDASHです(これは、Netflixが間もなく行うことです)。しかし、私たちはまだ皆が彼らのブラウザをアップグレードするのを待っています。また、Firefox用に別のDASH / VP9が必要になる可能性があります(open264については知っていますが、ひどいです。メインまたはハイプロファイルでビデオを実行できないため、現在は役に立ちません)。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加