[解決済み] Node.jsでhtml5ビデオプレーヤーにビデオファイルをストリーミングして、ビデオコントロールが動作し続けるようにするには?
質問
Tl;Dr - 質問です。
Node.jsでhtml5ビデオプレーヤーにビデオファイルをストリーミングする正しい方法は何でしょうか? を使用して、ビデオ コントロールが引き続き動作するように、html5 ビデオ プレーヤーにビデオ ファイルをストリーミングする正しい方法を教えてください。
I 思う は、ヘッダーの処理方法と関係があると思います。とにかく、以下はその背景情報です。このコードは 少し 長いですが、しかし、かなり簡単です。
Node を使用して小さな動画ファイルを HTML5 動画にストリーミングするのは簡単です。
小さな動画ファイルをとても簡単にHTML5ビデオプレーヤーにストリーミングする方法を学びました。 このセットアップでは、私の側で何もしなくてもコントロールが機能し、動画は完璧にストリームされます。サンプル動画を含む、完全に動作するコードのコピーは次のとおりです。 Google ドキュメントでダウンロードできます。 .
クライアントです。
<html>
<title>Welcome</title>
<body>
<video controls>
<source src="movie.mp4" type="video/mp4"/>
<source src="movie.webm" type="video/webm"/>
<source src="movie.ogg" type="video/ogg"/>
<!-- fallback -->
Your browser does not support the <code>video</code> element.
</video>
</body>
</html>
サーバです。
// Declare Vars & Read Files
var fs = require('fs'),
http = require('http'),
url = require('url'),
path = require('path');
var movie_webm, movie_mp4, movie_ogg;
// ... [snip] ... (Read index page)
fs.readFile(path.resolve(__dirname,"movie.mp4"), function (err, data) {
if (err) {
throw err;
}
movie_mp4 = data;
});
// ... [snip] ... (Read two other formats for the video)
// Serve & Stream Video
http.createServer(function (req, res) {
// ... [snip] ... (Serve client files)
var total;
if (reqResource == "/movie.mp4") {
total = movie_mp4.length;
}
// ... [snip] ... handle two other formats for the video
var range = req.headers.range;
var positions = range.replace(/bytes=/, "").split("-");
var start = parseInt(positions[0], 10);
var end = positions[1] ? parseInt(positions[1], 10) : total - 1;
var chunksize = (end - start) + 1;
if (reqResource == "/movie.mp4") {
res.writeHead(206, {
"Content-Range": "bytes " + start + "-" + end + "/" + total,
"Accept-Ranges": "bytes",
"Content-Length": chunksize,
"Content-Type": "video/mp4"
});
res.end(movie_mp4.slice(start, end + 1), "binary");
}
// ... [snip] ... handle two other formats for the video
}).listen(8888);
しかし、この方法は、ファイルサイズが1GBに制限されています。
ストリーミング (サイズ不問) のビデオ ファイルを
fs.createReadStream
を利用することで
fs.createReadStream()
を利用することで、サーバはファイルを一度にメモリに読み込むのではなく、 ストリームで読み込むことができるようになります。これは正しい方法のように聞こえますし、構文も非常にシンプルです。
サーバー スニペット。
movieStream = fs.createReadStream(pathToFile);
movieStream.on('open', function () {
res.writeHead(206, {
"Content-Range": "bytes " + start + "-" + end + "/" + total,
"Accept-Ranges": "bytes",
"Content-Length": chunksize,
"Content-Type": "video/mp4"
});
// This just pipes the read stream to the response object (which goes
//to the client)
movieStream.pipe(res);
});
movieStream.on('error', function (err) {
res.end(err);
});
これで動画が正常にストリーミングされるようになりました。しかし、ビデオ コントロールはもはや機能しません。
どのように解決するのですか?
この
Accept Ranges
ヘッダ(のビットは
writeHead()
の部分) は、HTML5 ビデオ コントロールを動作させるために必要です。
やみくもにフルファイルを送信するのではなく、まず最初に
Accept Ranges
ヘッダを確認し、その部分のみを読み込んで送信します。
fs.createReadStream
サポート
start
であり
end
というオプションがあります。
というわけで、例を試してみたところ、うまくいきました。コードはきれいではありませんが、理解するのは簡単です。まず、範囲ヘッダーを処理して、開始/終了位置を取得します。次に
fs.stat
を使って、ファイル全体をメモリに読み込むことなく、ファイルのサイズを取得します。最後に
fs.createReadStream
を使って、要求された部分をクライアントに送ります。
var fs = require("fs"),
http = require("http"),
url = require("url"),
path = require("path");
http.createServer(function (req, res) {
if (req.url != "/movie.mp4") {
res.writeHead(200, { "Content-Type": "text/html" });
res.end('<video src="http://localhost:8888/movie.mp4" controls></video>');
} else {
var file = path.resolve(__dirname,"movie.mp4");
fs.stat(file, function(err, stats) {
if (err) {
if (err.code === 'ENOENT') {
// 404 Error if file not found
return res.sendStatus(404);
}
res.end(err);
}
var range = req.headers.range;
if (!range) {
// 416 Wrong range
return res.sendStatus(416);
}
var positions = range.replace(/bytes=/, "").split("-");
var start = parseInt(positions[0], 10);
var total = stats.size;
var end = positions[1] ? parseInt(positions[1], 10) : total - 1;
var chunksize = (end - start) + 1;
res.writeHead(206, {
"Content-Range": "bytes " + start + "-" + end + "/" + total,
"Accept-Ranges": "bytes",
"Content-Length": chunksize,
"Content-Type": "video/mp4"
});
var stream = fs.createReadStream(file, { start: start, end: end })
.on("open", function() {
stream.pipe(res);
}).on("error", function(err) {
res.end(err);
});
});
}
}).listen(8888);
関連
-
[解決済み] Node.jsで現在のスクリプトのパスを取得するにはどうしたらいいですか?
-
[解決済み] Node.jsでファイルをダウンロードする方法(サードパーティライブラリを使用しない)?
-
[解決済み] jqueryでdivの要素がオーバーフローしていないかチェックする
-
[解決済み] 文字列が空白であるかどうかをチェックする
-
[解決済み] 無効になっている入力フィールドの値を送信する
-
[解決済み] サブドメインにまたがってlocalStorageを使用する
-
[解決済み] 各オブジェクトに?重複
-
[解決済み] 文字列とラベルのローカライズとグローバリゼーションのベストプラクティス【終了しました
-
[解決済み] Javascript の parseInt() で先頭のゼロを削除する。
-
[解決済み] jQueryのバージョン1、バージョン2、バージョン3の違いは何ですか?[クローズド]
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] JSのDateからDay名
-
[解決済み] 文字列のn番目の出現箇所を取得するには?
-
[解決済み] Reactコンポーネントでthis.setStateを複数回使用するとどうなりますか?
-
[解決済み] Reactメモを使うべきではない場合とは?
-
[解決済み] javascriptで文字列から関数を作成する方法はありますか?
-
[解決済み] $.ajax実行中にローディングイメージを表示する
-
[解決済み] Javascript の parseInt() で先頭のゼロを削除する。
-
[解決済み] jQueryを使用して、すべてのクリックイベントハンドラを削除するにはどうすればよいですか?
-
[解決済み] <ng-content>が空かどうかを確認する方法は?(これまでのAngular 2+で)
-
[解決済み] JavaScript で `throw` の後に `return` をする必要がありますか?