【问题标题】:How to send/"stream" video file to HTML5 video tag如何发送/“流”视频文件到 HTML5 视频标签
【发布时间】:2020-05-16 10:32:08
【问题描述】:

我尝试将视频文件发送到 HTML5 <video>-tag。
我发现了一个可以追溯到 2010 年(如果不是更早的话)的 sn-p。
它在整个 Internet 上被复制并且仍在流传,但在代码风格、名称、使用的 Node.js API 版本或库方面存在一些细微差别。
我有一些问题。
这是sn-p:

app.get('/video', function(req, res) {
    const path     = 'some_path/video.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)
    }
})

这里只是它的几个来源:

  1. Video streaming with HTML 5 via node.js
  2. video streaming in nodejs through fs module
  3. https://github.com/davidgatti/How-to-Stream-Movies-using-NodeJS/blob/master/routes/video.js
  4. https://github.com/tsukhu/rwd-spa-alljs-app/blob/master/routes/video_streamer.js
  5. https://tewarid.github.io/2011/04/25/stream-webm-file-to-chrome-using-node.js.html

很明显,这个 sn-p 还没有准备好生产:

  1. 它使用同步 statSync() 而不是异步版本,
  2. 不解析Range头部的完整语法,也没有错误处理,
  3. 它是硬编码的,
  4. else brunch 可能是多余的,

我对此没有意见。这是一个“入门级”代码。没关系。
但最重要的是,它没有按预期工作......但仍然有效

据我所知,浏览器以Range标头的形式发送对<video>-tag来源的请求

Range: bytes=12345-

并且,如果是初始请求,它将是

Range: bytes=0-

所以,这条线

const end = parts[1] ? parseInt(parts[1],10) : fileSize-1

等同于

const end = fileSize-1

而且,在初始请求中,服务器将发送的不是一小段视频,而是整个文件。并且在视频倒带的情况下 - 从请求的位置到结束的一个非常大的块。

如果您通过 Javascript 请求文件,这肯定不会按预期工作。您将等待文件完全加载,或者您需要以某种方式处理跟踪请求进度,这将导致相当复杂的客户端代码。
但它对<video>-tag 来说就像一个魅力,因为浏览器会代表你处理这个问题。

我们可以通过计算end 来解决这个问题:

const preferred_chunksize = 10000000 // arbitrary selected value
let end = parts[1] ? parseInt(parts[1],10) : start + preferred_chunksize
if( end > fileSize-1 ) end = fileSize-1

或者,考虑到用于<video>-tag 的Range 标头的形式,即使这样:

const preferred_chunksize = 10000000 // arbitrary selected value
let end = start + preferred_chunksize
if( end > fileSize-1 ) end = fileSize-1

好的,现在它确实发送了预期大小的部分响应。 但是

  1. 这些行比
    const end = fileSize-1
    
  2. 我们需要明智地选择preferred_chunksize。例如,像 preferred_chunksize=1000 这样的小块大小会发出大量请求,并且工作速度会明显变慢。
    虽然,至少在 Chrome 和 Firefox 中,原始版本的代码流视频文件非常好:我没有看到过多的缓存或内存使用,或速度问题。而且我无需担心preferred_chunksize 的价值。

所以我的问题是:我是否应该费心发送正确大小的块(如果我只需要将视频发送到<video>-tag),是否有任何流行的浏览器/客户端 js 库无法播放使用原始 sn-p 提供的视频文件?或者这个 sn-p 的方法是否有任何隐藏的问题?

【问题讨论】:

    标签: javascript node.js http-headers video-streaming html5-video


    【解决方案1】:

    服务器无法选择发送回客户端的字节范围。客户端发出请求,服务器必须满足它。发回其他任何内容都是错误的,浏览器可能不会播放视频

    视频标签没有发出 XHR 请求,它在下载完成之前访问了响应数据(您也可以通过流 apis 访问)。浏览器可以(并且经常这样做)通过关闭 TCP 会话来取消开放式范围请求(Range: bytes=0-)。当它请求Range: bytes=12345-这样的范围时,是因为它已经解析了视频文件头,并且它知道它需要的数据位于偏移量12345处。

    【讨论】:

    • sn-p 确实发送了一个具有正确起点的范围。浏览器不要求终点。正式服务器不会出错。但也不完全符合部分内容交付的想法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-11
    • 2012-08-01
    • 2011-07-11
    • 1970-01-01
    相关资源
    最近更新 更多