用Node.js处理将视频文件流传输到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(),服务器可以读取流中的文件,而不必一次将所有文件都读取到内存中。这听起来像做事的正确方法,语法非常简单:
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
writeHead()
我认为,不仅要盲目发送完整文件,还应首先检查Accept RangesREQUEST中的标头,然后读入并发送该位。fs.createReadStreamsupport start和end选项。
start
end
所以我尝试了一个例子,它可以工作。该代码不是很漂亮,但是很容易理解。首先,我们处理范围标头以获取开始/结束位置。然后,我们用于fs.stat获取文件的大小,而无需将整个文件读入内存。最后,用于fs.createReadStream将请求的部分发送给客户端。
fs.stat
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);