node.jsを使用してffmpegのリアルタイム出力をHTML5クライアントにストリーミングする最善の方法を理解するのにとても困っています、なぜならば、多くの変数が関係しており、私はこの分野で多くの経験を持っていません。 私のユースケースは
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});
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
});
EDIT 3:IOS10では、HLSは断片化されたmp4ファイルをサポートします。答えは フラッシュ、iOS9以下、IE10以下は存在しないものと考えてください。
EDIT 2: コメントでも指摘されていますが、状況は変わります。 ほぼ全てのブラウザがAVC/AACコーデックに対応します。 iOSはまだHLSが必要です。しかし、hls.jsのようなアダプターを介して、HLSを再生することができます。 MSEでHLSを再生することができます。iOSが必要であれば、HLS+hls.jsが新しい答えです。 フラグメントMP4(DASH)が必要です。
ビデオ、特にライブビデオが非常に難しい理由はたくさんあります。(元の質問では、HTML5 ビデオが必要と明記されていますが、質問者はコメントで Flash も可能と述べていることに注意してください。そのため、この質問は誤解を招く恐れがあります。)
最初にもう一度説明します。**HTML5を使ったライブストリーミングの公式サポートはありません。ハックする方法はありますが、その方法は様々です。
EDIT: この回答を書いた後、Media Source Extensionsは成熟しました。 Media Source Extensionsは成熟してきており、今では実行可能な選択肢の一つになりつつあります。ほとんどの主要なブラウザでサポートされており ほとんどの主要ブラウザでサポートされています。IOSはまだ対応していません。
次に、VOD(ビデオ・オン・デマンド)とライブ・ビデオは全く異なるものであることを理解する必要があります。確かに同じ映像ですが、問題点が異なるため、フォーマットも異なります。例えば、パソコンの時計が1%早くなっても、VODでは気がつきません。ライブ映像では、その前に映像を再生しようとすることになります。進行中のライブ映像に参加するには、デコーダーの初期化に必要なデータが必要なので、ストリームの中で繰り返すか、帯域外に送信する必要があります。VODでは、ファイルの先頭を読み込んで、好きなところまでシークすることができます。
さて、ここからは少し掘り下げてみましょう。
プラットフォーム
コーデック
ブラウザでのライブビデオの一般的な配信方法です。
ブラウザでのVODの一般的な配信方法。
html5のビデオタグです。
どのブラウザがどのフォーマットをサポートしているか見てみましょう。
Safariです。
ファイアフォックス
IE
クローム
MP4はライブビデオには使用できません(注:DASHはMP4のスーパーセットなので、混同しないように)。MP4はmoovとmdatの2つに分かれています。mdatには生のオーディオビデオデータが入っています。しかし、インデックスが付いていないので、moovがなければ意味がありません。moovには、mdatに含まれるすべてのデータのインデックスが含まれています。しかし、そのフォーマットのために、各フレームのタイムスタンプとサイズがわかるまでは 'flattened'することができません。フレームのサイズを偽装するmoovを作成することは可能かもしれませんが、帯域幅を非常に無駄にしてしまいます。
ですから、どこでも配信したいのであれば、最小公倍数を見つける必要があります。フラッシュに頼らずとも、ここにはLCDがないことがわかるでしょう。 の例です。
LCDに最も近い方法は、iOSユーザーにはHLSを使用し、それ以外のユーザーにはFlashを使用することです。 私の個人的な好みは、HLSをエンコードして、他のユーザーにはflashを使ってHLSを再生することです。HLSをフラッシュで再生するには、JW player 6を使用します(私のようにAS3でFLVに変換する独自のHLSを作成することもできます)。
近いうちに、iOS/MacではHLS、それ以外の場所ではMSE経由のDASHというのが最も一般的な方法になるでしょう(これはNetflixが近々行う予定です)。しかし、私たちはまだ皆さんがブラウザをアップグレードするのを待っています。また、Firefox用のDASH/VP9も別途必要になるでしょう(open264は知っていますが、最悪です。メインのビデオやハイプロファイルのビデオができません。だから今のところ使い物になりません)。)
これは複雑な問題で、多くのレイヤーがあり、ライブビデオを配信する前にすべてが機能していなければなりませんので、皆さん、特にszatmaryさん、ありがとうございます。私の最初の質問、HTML5ビデオの使用とフラッシュの比較を明確にすると、私のユースケースでは、汎用性があり、クライアントへの実装が容易で、将来性のあるHTML5を強く希望しています。Flashはその次という感じですので、この質問ではHTML5を選択します。
この演習を通して多くのことを学びましたが、ライブストリーミングはVOD(HTML5ビデオでうまく機能する)よりもはるかに難しいことは同意できます。しかし、私のユースケースではこれを満足に動作させることができました。MSE、flash、Nodeでの凝ったバッファリングスキームなど、より複雑なオプションを追求した結果、解決策は非常にシンプルなものになりました。問題は、FFMPEGが断片化されたMP4を破損していたことで、FFMPEGのパラメータを調整する必要がありましたが、もともと使っていたhttp上の標準的なノード・ストリーム・パイプ・リダイレクトが必要だったのです。
MP4には 'fragmentation'というオプションがあり、MP4をより小さなフラグメントに分割して独自のインデックスを持ち、MP4のライブストリーミングのオプションを実現しています。しかし、ストリームに戻ってシークすることはできず(私のユースケースではOK)、FFMPEGの後期バージョンではフラグメンテーションをサポートしています。
タイミングが問題になることもあり、私のソリューションでは、リミックス(事実上、FFMPEGはライブストリームを受信し、それをリミックスし、HTTPで提供するためにノードに送信しなければなりません)の組み合わせにより、2〜6秒のラグが発生します。これについてはあまり対処できませんが、Chromeではビデオができるだけ追いつこうとするため、ビデオが少し飛びますが、IE11(私が好んで使用するクライアント)よりも最新の状態になります。
この記事でコードの仕組みを説明するよりも、コメント付きのGISTをご覧ください(クライアントのコードは含まれておらず、標準的なHTML5のvideoタグとノードのhttpサーバーアドレスです)。GISTはこちら: https://gist.github.com/deandob/9240090
このユースケースの類似例を見つけることができませんでしたので、上記の説明とコードが他の方のお役に立てれば幸いです。
これは私の特定の質問に対する回答ですが、szatmary'さんの回答が最も包括的であるため、受け入れられるものとして選択しました。