有人问我怎么用Node.js进行视频流传输。这我还真没试过,所以话都说到这了当然要试一下!以下是我的发现。
用Node.js进行视频流传输的挑战在于要创建一个路径,该路径能传输一个mp4文件到页面上并使视频可供观看。
以下是我的分解方法:
- 创建服务器路径以发送视频。
- 用HTML5和JS请求提要。
- 分批加载视频,而不是从头开始加载。
TL; DR-你可以在此处找到一个有效的视频流demo。
流
视频通过流媒体播放,这意味着,你不应该把所有内容都放在一个单一的包里发到前端,而是应该一次只发一小块。
通过流发送意味着在能看视频之前,不用非等着页面从服务器下载完整个视频,你可以在视频开始的前几秒钟请求服务器,然后就可以边放视频边下载了。
这个方法还适用于发送大篇幅文本。例如,你的用户无需等待,就能看到文章的前几行。
用到的原理
-
获取文件大小 :Node中的
fs
有一个名为statSync
的方法,该方法将返回文件的统计信息。在这些统计数据中,我们需要知道在当前加载的chunk到达文件末端时文件的大小。你也可以用stat
-拿我来说,我试图避免同步性,这样能使新手更容易理解代码。 -
从文件中创建流 :
fs
包含另一个名为createReadStream
的方法,该方法将在给定文件(开始和结束语块)的情况下创建流。
const fileChunk = fs.createReadStream(sample.mp4, {start, end});
- 块的大小: 在请求中会为你提供起始块。为了弄清楚要加载多少文件,我对结束块的大小(如果不可用,可用完整的文件大小)和起始块的大小进行了减法运算:
endChunk - startChunk
- HTTP 206: 这用于我们所希望的连接头的部分内容。我们不断向前端提供分块,并希望在发出请求时使起始分块可用。你至少要下定义:
'Content-Range': 'bytes chunkStart-chunkEnd/chunkSize'
'Accept-Ranges': 'bytes'
'Content-Length': chunkSize
'Content-Type': 'video/mp4'
服务器
考虑到这些因素,我在名为 video
的路径中结束了类似的内容。(我正在使用Express创建路径。)
app.get('/video', function(req, res) {
const path = 'assets/sample.mp4'
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)
}
});
虽然有很多代码,但别担心,你能随时用 demo进行进一步调试。
我来解释下流程
- 提出请求后,我们会获取文件的大小,并在
else
语句中发送视频的前几个语块。 - 当我们开始看视频时(通过访问通过
localhost:3000/video
或前端的路径),会发出后续请求,这次标好头中的范围,以便我们知道下一个语块的起点。 - 再次读取文件来创建另一个流,将开始和结束的新值(很有可能是请求头中出现的当前部分以及视频的文件大小)传递给我们。
- 通过应用前面讨论的公式,我们将206标头响应设置为仅发送部分新生成的流。
前端
用HTML5 video
标签的前端非常容易——你只需添加源路径,它就会帮你解决剩下的事。
<video id="videoPlayer" controls>
<source src="http://localhost:3000/video" type="video/mp4">
</video>
该 controls
属性让你可以查看播放器的控件。
如果没有它,你可以通过访问播放器组件代替自己编写这些属性和其他属性。在这种情况下,id是 videoPlayer
。
因此,如果HTML上有一个按钮,则可以执行类似的操作来复制播放/停止按钮:
在你的开发者工具里的 network
选项卡中,你可以看到分块流媒体,尤其是在节流连接的情况下。
结束语
我并不期待这么简单的操作实施起来能十分顺利。当然,实施中也存在一些缺陷,例如起始块总是超出预期(可能是因为挂着的连接,我在此示例中没有进行处理)。
尽管如此,对于那些想开始创建某种流媒体应用程序的人来说,我认为这是一个很好的起点。如果你有更好或不容易出错的方法,请告诉我!
作者 Diogo Spínola
原文链接 https://medium.com/better-programming/video-stream-with-node-js-and-html5-320b3191a6b6