Node.jsを使用して、HTML5ビデオタグへのビデオのストリーミングをサポートするWebサーバーをセットアップしようとしています。これまでの私のコードは次のとおりです。
var range = request.headers.range;
var total = file.length;
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-1;
var chunksize = (end-start)+1;
response.writeHead(206, { "Content-Range": "bytes " + start + "-" + end + "/" + total, "Accept-Ranges": "bytes", "Content-Length": chunksize, "Content-Type": type });
response.end(file);
「request」はhttpリクエストを表し、typeは「application/ogg」または「video/ogg」(両方とも試しました)で、「file」はファイルシステムから読み取られた.ogvファイルです。応答ヘッダーは次のとおりです。
Content-Range bytes 0-14270463/14270464
Accept-Ranges bytes
Content-Length 14270464
Connection keep-alive
Content-Type video/ogg
応答ヘッダーを調べたところ、このコードは正常に機能しているように見えますが、いくつかの問題があります。
Node.jsを介してビデオストリーミングを動作させるために私ができることについて誰にもアイデアがありますか?
ありがとう!
クリス
Nodejsフォーラムの助けを借りて、これを機能させることができました。
http://groups.google.com/group/nodejs/browse_thread/thread/8339e0dc825c057f/822b2dd48f36e89
Googleグループスレッドのハイライト:
Google chromeは、最初に範囲0〜1024で要求を行い、次に範囲 "1024-"を要求することが知られています。
response.end(file.slice(start、chunksize)、 "binary");
次に:
「接続」ヘッダーを「閉じる」に設定することで、Firefoxで問題なくビデオを再生できるようになりました
次に:
あなたは間違ってコンテンツ長を計算しているようです:
var chunksize =(end-start)+1;
Startが0でendが1の場合、チャンクサイズは2であり、1でなければなりません。
私はこれが本当に古い質問であることを知っていますが、Googleがそれを好むように思えるので、私はNode.jsを書いたことを指摘する価値があると思いました ビデオストリーミングモジュール (Github、またはNPM経由)見てみる価値もあります。
このソリューションは、サーバー側のビデオまたはオーディオメディアファイルの非同期読み取りを行います... URLにあるnodejsサーバーを起動します。
また、クライアント側のHTML5(ブラウザー/アプリ)のUIウィジェットのスライダーの動きを前後に正しく処理します。
以下のコードスニペットをサーバー側ファイルとして保存します。
media_server.js
...を使用してサーバー側で実行します
node media_server.js
楽しい
var http = require('http'),
fs = require('fs'),
util = require('util');
var path = "/path/to/local/video/or/audio/file/on/server.mp4";
var port = 8888;
var Host = "localhost";
http.createServer(function (req, res) {
var stat = fs.statSync(path);
var total = stat.size;
if (req.headers.range) { // meaning client (browser) has moved the forward/back slider
// which has sent this request back to this server logic ... cool
var range = req.headers.range;
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-1;
var chunksize = (end-start)+1;
console.log('RANGE: ' + start + ' - ' + end + ' = ' + chunksize);
var file = fs.createReadStream(path, {start: start, end: end});
res.writeHead(206, { 'Content-Range': 'bytes ' + start + '-' + end + '/' + total, 'Accept-Ranges': 'bytes', 'Content-Length': chunksize, 'Content-Type': 'video/mp4' });
file.pipe(res);
} else {
console.log('ALL: ' + total);
res.writeHead(200, { 'Content-Length': total, 'Content-Type': 'video/mp4' });
fs.createReadStream(path).pipe(res);
}
}).listen(port, Host);
console.log("Server running at http://" + Host + ":" + port + "/");
Sam9291の答えに基づいて、createReadStream()
を使用して関数を書き直し、いくつかの問題を修正しました。
/**
* Sends a static file to the HTTP client, supporting partial transfers.
*
* @req HTTP request object
* @res HTTP response object
* @fn Path to file that should be sent
* @contentType MIME type for the response (defaults to HTML)
*/
function sendFile(req, res, fn, contentType) {
contentType = contentType || "text/html";
fs.stat(fn, function(err, stats) {
var headers;
if (err) {
res.writeHead(404, {"Content-Type":"text/plain"});
res.end("Could not read file");
return;
}
var range = req.headers.range || "";
var total = stats.size;
if (range) {
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-1;
var chunksize = (end-start)+1;
headers = {
"Content-Range": "bytes " + start + "-" + end + "/" + total,
"Accept-Ranges": "bytes",
"Content-Length": chunksize,
"Content-Type": contentType
};
res.writeHead(206, headers);
} else {
headers = {
"Accept-Ranges": "bytes",
"Content-Length": stats.size,
"Content-Type": contentType
};
res.writeHead(200, headers);
}
var readStream = fs.createReadStream(fn, {start:start, end:end});
readStream.pipe(res);
});
}
Node.jsの上にMVCフレームワークsails.jsを使用していますが、次のコードで問題なく動作しました。
/**
* VideoController
*
* @module :: Controller
* @description :: Contains logic for handling requests.
*/
var fs = require('fs');
module.exports = {
/* e.g.
sayHello: function (req, res) {
res.send('hello world!');
}
*/
/**
* /video/stream
*/
stream: function (req,res) {
// This will render the view:
// C:\Users\sam\Documents\Dev\Fun\mymoviebank/views/video/stream.ejs
res.view();
},
play: function (req,res) {
fs.readFile('/Users/sam/Videos/big_buck_bunny.mp4', function (err, data) {
if (err) throw err;
var range = req.headers.range;
var total = data.length;
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-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' });
res.end(data);
});
}
};
お役に立てれば
私はこのソリューションがより簡単で、(チェックされた答えとは異なり)うまくいくように見えました。 (私はそのスレッドの最後でcoffeescriptソリューションを適応させようとしましたが、最初のリクエスト(「bytes = 0-」)がそれを爆破するという事実に対処すると、それは一種の働きをしました。
http://elegantcode.com/2011/04/06/taking-baby-steps-with-node-js-pumping-data-between-streams/
私の実際の実装:
function stream_response( res, file_path, content_type ){
var readStream = fs.createReadStream(file_path);
readStream.on('data', function(data) {
var flushed = res.write(data);
// Pause the read stream when the write stream gets saturated
console.log( 'streaming data', file_path );
if(!flushed){
readStream.pause();
}
});
res.on('drain', function() {
// Resume the read stream when the write stream gets hungry
readStream.resume();
});
readStream.on('end', function() {
res.end();
});
readStream.on('error', function(err) {
console.error('Exception', err, 'while streaming', file_path);
res.end();
});
res.writeHead(200, {'Content-Type': content_type});
}
エクスプレスを使用する場合、media_server.jsまたはindex.jsにこれを入れて、ポート3000でメディアを提供します
const express = require('express')
const fs = require('fs')
const path = require('path')
const app = express()
app.use(express.static(path.join(__dirname, 'public')))
app.get('/', function(req, res) {
res.sendFile(path.join(__dirname + '/index.html'))
})
app.get('/video', function(req, res) {
const path = 'assets/sample.mp4'// your video path
const stat = fs.statSync(path)
const fileSize = stat.size
const range = req.headers.range
if (range) {
const parts = range.replace(/bytes=/, "").split("-")
const start = parseInt(parts[0], 10)
const end = parts[1]
? parseInt(parts[1], 10)
: fileSize-1
const chunksize = (end-start)+1
const file = fs.createReadStream(path, {start, end})
const head = {
'Content-Range': `bytes ${start}-${end}/${fileSize}`,
'Accept-Ranges': 'bytes',
'Content-Length': chunksize,
'Content-Type': 'video/mp4',
}
res.writeHead(206, head)
file.pipe(res)
} else {
const head = {
'Content-Length': fileSize,
'Content-Type': 'video/mp4',
}
res.writeHead(200, head)
fs.createReadStream(path).pipe(res)
}
})
app.listen(3000, function () {
console.log('Listening on port 3000!')
})
その後、index.html
<html>
<head>
<title>Video stream sample</title>
</head>
<body>
<video id="videoPlayer" controls muted="muted" autoplay>
<source src="http://localhost:3000/video" type="video/mp4">
</video>
</body>
</html>