通过node.js与HTML 5进行videostream

我正在尝试使用node.js设置一个支持stream式video到HTML5video标签的networking服务器。 这是我的代码到目前为止:

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); 

其中“请求”代表http请求,类​​型是“应用程序/ ogg”或“video/ ogg”(我已经试过),“文件”是从文件系统中读取的.ogv文件。 以下是响应标题:

 Content-Range bytes 0-14270463/14270464 Accept-Ranges bytes Content-Length 14270464 Connection keep-alive Content-Type video/ogg 

我已经检查了响应头,这段代码看起来工作正常,但是有几个问题:

  1. 在本地networking上,video的加载非常缓慢。 从我可以告诉检查使用萤火虫的反应,该文件似乎stream入约150 kb /秒。
  2. video根本不播放。 即使我等待整个事情加载,HTML 5video标签只显示一个大的“X”,而不是在Firefox的电影。

有没有人有什么想法,我可以做什么来通过node.jsvideostream工作?

谢谢!
克里斯

我知道这是一个非常古老的问题,但是Google似乎喜欢它,所以我认为值得指出的是我写了一个Node.js videostream模块 (Github,或者通过NPM),希望值得一看。

我能够得到这个来自nodejs论坛的一些帮助:

http://groups.google.com/group/nodejs/browse_thread/thread/8339e0dc825c057f/822b2dd48f36e890

Google网上论坛主题的亮点:

已知谷歌浏览器首先发出范围为0-1024的请求,然后请求范围“1024-”。

response.end(file.slice(start,chunksize),“binary”);

然后:

我能够通过设置“连接”标题“closures”,让video在Firefox中没有问题,

然后:

似乎你错误地计算内容长度:

var chunksize =(end-start)+1;

如果start是0,end是1,那么chunksize是2,应该是1。

这个解决scheme做一个服务器端video或audio媒体文件的asynchronous读取…它在一个可见的URL在一个nodejs服务器上旋转

HTTP://本地主机:8888 /

也正确处理客户端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 = { /* eg 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); }); } }; 

希望这可以帮助

我发现这个解决scheme似乎更简单,(不像检查的答案)适合我。 (我尝试了在该线程末尾调整coffeescript解决scheme,并且在处理初始请求(对于“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}); }