Video 通过node.js使用HTML 5进行视频流
我正在尝试设置一个web服务器,该服务器将支持使用node.js将视频流传输到HTML5视频标签。以下是我目前的代码:Video 通过node.js使用HTML 5进行视频流,video,html,stream,node.js,Video,Html,Stream,Node.js,我正在尝试设置一个web服务器,该服务器将支持使用node.js将视频流传输到HTML5视频标签。以下是我目前的代码: 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
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请求,类型为“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
我已经检查了响应头,这段代码似乎工作正常,但有几个问题:
/**
* 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);
});
}
Chris在nodejs论坛的帮助下,我成功地实现了这一点: Google Groups线程的亮点: 众所周知,Google chrome首先会发出范围为0-1024的请求 然后请求范围“1024-” response.end(file.slice(start,chunksize),“binary”) 然后: 我能够让视频在firefox中播放,通过设置 将“连接”标题改为“关闭” 然后: 您似乎没有正确计算内容长度: var chunksize=(结束-开始)+1 如果start是0,end是1,那么在您的例子中chunksize是2,并且应该是 是1
我知道这是一个非常古老的问题,但由于谷歌似乎喜欢它,我认为值得指出的是,我编写了一个Node.js(Github,或通过NPM),希望也值得一看 我在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-”)将其破坏的事实后,它就开始工作了 我的实际执行情况:
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});
}
此解决方案对服务器端视频或音频媒体文件进行异步读取…它在可见的URL处启动nodejs服务器 此外,它还能正确处理客户端HTML5(浏览器/应用程序)的前向/后向UI小部件滑块移动 将以下代码段另存为服务器端文件:
media_server.js
…在服务器端使用
node media_server.js
享受
根据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);
});
}
使用express时,将其放入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>
视频流样本
我发现这个codesandbox,看起来非常有用
我喜欢这样的答案!谢谢。:)所以两年后再回到这个话题上来……:)是否有一种方法可以使用该脚本发送udp接收到的实时数据(到浏览器)?@randomuser1恐怕没有。实时流媒体(例如,需要分割输入,支持索引文件)是脚本不做的。我很想支持这一点,但不幸的是,我没有时间去做。抱歉。嘿@Melonchly,实际上我已经做了所有的事情——我“分割”了输入,放了一些索引,然后通过UDP发送。我现在可以在Node.js中阅读它们,我看到了它们——每个片段的编号及其内容。但我只在控制台中看到它,我想在用户浏览器的另一个站点上合并它-我不知道这是否可能。Enpm的fs.statSync(../file/path/…)负责处理细节。看,这个解决方案不会扩展-它将整个视频文件拉入内存,只为其中的一小部分提供服务
fs.createReadStream(文件{start:$start,end:#end})
将允许您将流传输到响应,而不需要将整个视频文件加载到内存中(想象一下,如果1000个用户同时这样做的话)。这对流媒体很好。。。然而,它需要处理request.headers,以便对客户端小部件请求做出反应,例如在源媒体上向前/向后跳过。。。很好,有没有一种方法可以将视频文件从udp流传输到浏览器,而不是源文件?我们可以从express进行吗?Node noob这里:)我相信chunksize是正确的。根据:字节范围规范中的第一个字节位置值给出了范围中第一个字节的字节偏移量。最后一个字节的pos值给出该范围内最后一个字节的字节偏移量;也就是说,指定的字节位置是包含的。字节偏移量从零开始。