简体   繁体   English

Node.js / Express视频流(HTTP 206部分内容)

[英]Node.js / Express video streaming (HTTP 206 Partial Content)

I have a binary document (mp4 video file) in a database (MarkLogic). 我在数据库(MarkLogic)中有一个二进制文件(mp4视频文件)。 I am using the database's Node.js API to stream the document in chunks. 我正在使用数据库的Node.js API以块的形式传输文档。 The setup looks like this: 设置如下所示:

html file html文件

<video controls="controls" width="600">
  <source src="/video/myvideo.mp4" type="video/mp4">
</video>

In express I have then setup a route which handles the /video/:param route (in the database the video has the unique identifier which is the string '/video/myvideo.mp4') 然后在Express中我设置了一个处理/ video /:param路由的路由(在数据库中,视频有唯一的标识符,即字符串'/video/myvideo.mp4')

node.js 的node.js

// I'm only showing the relevant things in here

const serveVideo = (req, res) => {
  var stream = db.documents.read('/gopro/malta.mp4').stream('chunked');

  var chunks = [];
  var chunkBytes = 0;
  var start = 0;
  stream.on('data', (chunk) => {
    var headers;
    var range = req.headers.range;
    var total = 214335483; //total length of vid in bytes

    if (range) {
      var chunkSize = chunk.length;
      // (start === 0) ? start = 0 : start += chunkBytes;
      if (chunkBytes === 0) {
        start = 0
      } else {
        start = chunkBytes + 1
      }
      chunkBytes += chunkSize;

      headers = {
        'Content-Range': 'bytes ' + start + '-' + chunkBytes + '/' + total,
        'Accept-Ranges': 'bytes',
        'Content-Length': chunkSize,
        'Content-Type': 'video/mp4'
      };
      res.writeHead(206, headers);
      chunks.push(chunk);
    }
  });
  stream.on('end', () => {
    var allChunks = Buffer.concat(chunks);
    res.end(allChunks);
  });
});


router.route('/video/:uri').get(serveVideo);

Now of course the above fails with 'Error: Can't set headers after they are sent.' 当然,上面的错误是“错误:发送后无法设置标头”。 which is all fair and square. 这都是公平的。 But I can't get my head around this - the .stream('chunked') call forces the database to retrieve the document in chunks and I do see those chunks just fine, however how can I return a 206 for the browser? 但我无法理解这一点 - .stream('chunked')调用强制数据库以块的形式检索文档,我确实看到那些块很好,但是如何为浏览器返回206呢? I can't do it in the .on('data') as data is being streamed so the header would be sent multiple times. 我无法在.on('data')中执行此操作,因为数据正在流式传输,因此标头将被多次发送。 I guess which database I'm using is not really relevant - I would like to understand the concept, or at least see what I'm doing wrong. 我想我正在使用的数据库并不是真正相关的 - 我想理解这个概念,或者至少看看我做错了什么。

Any help is appreciated. 任何帮助表示赞赏。 All the examples and other discussions that I have seen that stream video using Node.js are reading the video file from disk. 我见过的所有使用Node.js流视频的示例和其他讨论都是从磁盘读取视频文件。

update 更新

Making a change to the code now allows FF to play the video but not Chrome: 现在更改代码允许FF播放视频而不是Chrome:

let stream = db.documents.read({uris:'/gopro/malta.mp4'}).stream('chunked');
stream.pipe(res);

There are no errors in Chrome's console. Chrome控制台中没有错误。 Here are the header details - note that there are two requests for the mp4 file: 以下是标题详细信息 - 请注意,mp4文件有两个请求:

1st 1

Response Headers
Connection:keep-alive
Date:Sat, 21 May 2016 17:05:30 GMT
Transfer-Encoding:chunked
X-Powered-By:Express

Request Headers
view source
Accept:*/*
Accept-Encoding:identity;q=1, *;q=0
Accept-Language:en-US,en;q=0.8,hu;q=0.6,ro;q=0.4,it;q=0.2
Cache-Control:no-cache
Connection:keep-alive
Cookie:__distillery=v20150227_a8e22306-65b3-4c2e-9a8a-159e308156ad; __smToken=7nYU8NYQY15mPowjjCZsS5D3
DNT:1
Host:localhost:8080
Pragma:no-cache
Range:bytes=0-
Referer:http://localhost:8080/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36

2nd 第2

Response Headers
Connection:keep-alive
Date:Sat, 21 May 2016 17:05:31 GMT
Transfer-Encoding:chunked
X-Powered-By:Express

Request Headers
view source
Accept:*/*
Accept-Encoding:identity;q=1, *;q=0
Accept-Language:en-US,en;q=0.8,hu;q=0.6,ro;q=0.4,it;q=0.2
Cache-Control:no-cache
Connection:keep-alive
Cookie:__distillery=v20150227_a8e22306-65b3-4c2e-9a8a-159e308156ad; __smToken=7nYU8NYQY15mPowjjCZsS5D3
DNT:1
Host:localhost:8080
Pragma:no-cache
Range:bytes=28-
Referer:http://localhost:8080/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36

The on('data') callback could always have a closure over an isFirstChunk variable that is initialized to true and have a test that, if isFirstChunk is true, emits the headers and sets isFirstChunk to false. on('data')回调可以始终对isFirstChunk变量进行闭包,该变量初始化为true,并且如果isFirstChunk为true,则测试将发出标头并将isFirstChunk设置为false。

It would be preferable to pipe the stream if possible. 如果可能的话,最好管道流。 npm might offer a stream library (maybe even through2()?) that has an event when the first data arrives. npm可能会提供一个流库(甚至可能是to2()?),它在第一个数据到达时有一个事件。

For the longer term, you could reasonably file an RFE for an event at the start of a stream. 从长远来看,您可以合理地在流的开头为事件提交RFE。

Hoping that helps, 希望有所帮助,

You most likely don't need all the chunk stuff. 你很可能不需要所有大块的东西。 Set headers manually: 手动设置标头:

res.status(206);

Then simply pipe the response: 然后只需管道响应:

let stream = db.yourChunkStuff();
stream.pipe(res);

The simplest way to is pipe streams. 最简单的方法是管道流。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM