使用Node.js和HTML5的视频流

1_eu1SkzhIVtLKgGB5vvG7Jg
有人问我怎么用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 属性让你可以查看播放器的控件。

1_bchK19b3xAbDaJegZIOe_w

如果没有它,你可以通过访问播放器组件代替自己编写这些属性和其他属性。在这种情况下,id是 videoPlayer

因此,如果HTML上有一个按钮,则可以执行类似的操作来复制播放/停止按钮:

在你的开发者工具里的 network 选项卡中,你可以看到分块流媒体,尤其是在节流连接的情况下。

结束语

我并不期待这么简单的操作实施起来能十分顺利。当然,实施中也存在一些缺陷,例如起始块总是超出预期(可能是因为挂着的连接,我在此示例中没有进行处理)。

尽管如此,对于那些想开始创建某种流媒体应用程序的人来说,我认为这是一个很好的起点。如果你有更好或不容易出错的方法,请告诉我!

作者 Diogo Spínola

原文链接 https://medium.com/better-programming/video-stream-with-node-js-and-html5-320b3191a6b6