通过 node.js 使用 HTML 5 进行视频流

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4360060/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-29 05:36:19  来源:igfitidea点击:

Video streaming with HTML 5 via node.js

videohtmlstreamnode.js

提问by Chris Harrington

I'm trying to set up a web server that will support streaming video to an HTML5 video tag using node.js. Here's my code so far:

我正在尝试设置一个 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(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);

Where "request" represents the http request, type is either "application/ogg" or "video/ogg" (I've tried both) and "file" is the .ogv file that's been read from the file system. Here are the response headers:

其中“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

I've examined the response headers and this code appears to be working fine, but there are a couple of problems:

我检查了响应头,这段代码似乎工作正常,但有几个问题:

  1. The video appears to load very slowly for being on a local network. From what I can tell examining the response using firebug, the file appears to be streamed in at about 150 kb/sec.
  2. The video doesn't play at all. Even if I wait for the whole thing to load, the HTML 5 video tag just shows a big "x" instead of a movie in firefox.
  1. 由于在本地网络上,视频的加载速度似乎非常缓慢。据我所知,使用 firebug 检查响应,文件似乎以大约 150 kb/秒的速度流入。
  2. 视频根本无法播放。即使我等待整个内容加载完毕,HTML 5 视频标签也只会在 Firefox 中显示一个大的“x”而不是电影。

Does anyone have any ideas as to what I can do to get video streaming working via node.js?

有没有人知道我可以做些什么来通过 node.js 使视频流工作?

Thanks!
Chris

谢谢!
克里斯

采纳答案by Chris Harrington

I was able to get this to work with some help from the nodejs forums:

我能够在 nodejs 论坛的一些帮助下让它工作:

http://groups.google.com/group/nodejs/browse_thread/thread/8339e0dc825c057f/822b2dd48f36e890

http://groups.google.com/group/nodejs/browse_thread/thread/8339e0dc825c057f/822b2dd48f36e890

Highlights from the Google Groups thread:

Google 网上论坛主题的亮点:

Google chrome is known to first make a request with the range 0-1024 and then request the range "1024-".

response.end(file.slice(start, chunksize), "binary");

众所周知,谷歌浏览器首先使用范围 0-1024 发出请求,然后请求范围“1024-”。

response.end(file.slice(start, chunksize), "binary");

Then:

然后:

I was able to get the video to play no problems in firefox by setting the "connection" header to "close"

通过将“连接”标题设置为“关闭”,我能够让视频在 Firefox 中播放没有问题

Then:

然后:

Seems that you are incorrectly computing the content-length:

var chunksize = (end-start)+1;

If start is 0 and end is 1, in your case chunksize is 2, and it should be 1.

似乎您错误地计算了内容长度:

var chunksize = (end-start)+1;

如果 start 是 0,end 是 1,那么在你的情况下,chunksize 是 2,它应该是 1。

回答by meloncholy

I know this is a really old question, but as Google seems to like it I thought it would be worth pointing out that I wrote a Node.js video streaming module(Github, or via NPM) that's hopefully worth a look too.

我知道这是一个非常古老的问题,但由于 Google 似乎喜欢它,我认为值得指出的是,我编写了一个 Node.js视频流模块(Github,或通过 NPM),希望也值得一看。

回答by Scott Stensland

This solution does an asynchronous read of a server side video or audio media file ... it spins up a nodejs server at URL visible at

此解决方案对服务器端视频或音频媒体文件进行异步读取......它在 URL 可见处启动 nodejs 服务器

http://localhost:8888/

http://localhost:8888/

also it correctly handles client side HTML5 (browser/app) forward/back UI widget slider movements

它还可以正确处理客户端 HTML5(浏览器/应用程序)前进/后退 UI 小部件滑块移动

save below code snippet as server side file :

将以下代码片段保存为服务器端文件:

media_server.js

... execute it on server side using

...在服务器端执行它使用

node media_server.js

enjoy

请享用

var http = require('http'),
    fs = require('fs'),
    util = require('util');

var path = "/path/to/local/video/or/audio/file/on/server.mp4";

var port = 8888;
var host = "localhost";

http.createServer(function (req, res) {

  var stat = fs.statSync(path);
  var total = stat.size;

  if (req.headers.range) {   // meaning client (browser) has moved the forward/back slider
                                         // which has sent this request back to this server logic ... cool
    var range = req.headers.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;
    console.log('RANGE: ' + start + ' - ' + end + ' = ' + chunksize);

    var file = fs.createReadStream(path, {start: start, end: end});
    res.writeHead(206, { 'Content-Range': 'bytes ' + start + '-' + end + '/' + total, 'Accept-Ranges': 'bytes', 'Content-Length': chunksize, 'Content-Type': 'video/mp4' });
    file.pipe(res);

  } else {

    console.log('ALL: ' + total);
    res.writeHead(200, { 'Content-Length': total, 'Content-Type': 'video/mp4' });
    fs.createReadStream(path).pipe(res);
  }
}).listen(port, host);

console.log("Server running at http://" + host + ":" + port + "/");

回答by Udo G

Based on Sam9291's answer, I rewrote the function using createReadStream()and fixing some problems:

根据 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);    

  });

}

回答by Samuel Ptheitroadier

I am using the MVC framework sails.json top of Node.jsand I managed to get it working fine with the following code:

我在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);

    });

  }

};

Hope this helps

希望这可以帮助

回答by podperson

I found this solution which seems to be simpler and (unlike the checked answer) works for me. (I tried adapting the coffeescript solution at the end of that thread and it kind of worked once I dealt with the fact that the initial request (for "bytes=0-") blows it up.

我发现这个解决方案似乎更简单,并且(与检查的答案不同)对我有用。(我尝试在该线程的末尾调整 coffeescript 解决方案,一旦我处理了初始请求(对于“bytes=0-”)将其炸毁的事实,它就起作用了。

http://elegantcode.com/2011/04/06/taking-baby-steps-with-node-js-pumping-data-between-streams/

http://elegantcode.com/2011/04/06/taking-baby-steps-with-node-js-pumping-data-between-streams/

My actual implementation:

我的实际实现:

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});
}

回答by Kevin Muchwat

when using express put this in your media_server.js or index.js which will serve the media on port 3000

使用 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!')
})

then in your index.html

然后在你的 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>

回答by Islem Penywis

I found this codesandboxand it seems really helpful https://codesandbox.io/s/14n6q1yr33

我找到了这个代码盒子,它似乎真的很有帮助 https://codesandbox.io/s/14n6q1yr33