首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何使用node.js将当前正在下载的视频流式传输到html5播放器?

如何使用node.js将当前正在下载的视频流式传输到html5播放器?
EN

Stack Overflow用户
提问于 2016-11-23 21:05:59
回答 1查看 1.8K关注 0票数 2

我目前正在做一个下载torrent的项目,在下载的同时,将其流式传输到html5视频播放器中。由于项目的限制,我不能使用peerflix或webtorrent。我目前使用的是torrent-stream模块。我的代码是node.js和pug。具体如下:

server.js

代码语言:javascript
运行
复制
var uri = 'magnet:?xt=urn:btih:11a2ac68a11634e980f265cb1433c599d017a759';

var engine = torrentStream(uri);    

let engineGo = function () {
      return new Promise(function (resolve, reject) {
        var engine = torrentStream(uri);
        engine.on('ready', function() {
            engine.files.forEach(function(file) {
              if (file.name.substr(file.name.length - 3) == 'mkv' || file.name.substr(file.name.length - 3) == 'mp4') {
                console.log('filename:', file.name);
                var stream = file.createReadStream();
                var writable = fs.createWriteStream(file.name);
                stream.pipe(writable);
                engine.on('download', function () {
                  console.log(file.name);
                  console.log(engine.swarm.downloaded / file.length * 100 + "%");
                  resolve(file);
                });
              }
           });
       });
     });
}

app.get('*', function (req, res) {
    if (req.url != "/Guardians.of.the.Galaxy.2014.1080p.BluRay.x264.YIFY.mp4") {
      var rpath = __dirname + '/views/index.pug';
      fs.readFile(rpath, 'utf8', function (err, str) {
        var fn = pug.compile(str, { filename: rpath, pretty: true});
        res.writeHead(200, { "Content-Type": "text/html" });
        res.write(fn());
        res.end();
      });
    } else {
      engineGo().then(function (result) {
        var filer = path.resolve(__dirname,result.name);
        fs.stat(filer, function(err, stats) {
          if (err) {
            if (err.code === 'ENOENT') {
              // 404 Error if file not found
              return res.sendStatus(404);
            }
          res.end(err);
          }
          var range = req.headers.range;
          if (!range) {
           // 416 Wrong range
           return res.sendStatus(416);
          }

          var positions = range.replace(/bytes=/, "").split("-");
          var start = parseInt(positions[0], 10);
          var total = stats.size;
          var end = positions[1] ? parseInt(positions[1], 10) : total - 1;
          var chunksize = (end - start) + 1;

          res.writeHead(206, {
            "Content-Range": "bytes " + start + "-" + end + "/" + total,
            "Accept-Ranges": "bytes",
            'Connection': 'keep-alive',
            "Content-Length": chunksize,
            "Content-Type": "video/mp4"
          });
          var stream = fs.createReadStream(filer, { start: start, end: end })
          .on("open", function() {
             stream.pipe(res);
          }).on("data", function (data) {
             console.log(data);
          }).on("error", function(err) {
             res.end(err);
          });
        });
      });
    }
});

index.pug

代码语言:javascript
运行
复制
doctype
html
  title
    |Welcome
  body
      video(id='video' src="http://localhost:8888/Guardians.of.the.Galaxy.2014.1080p.BluRay.x264.YIFY.mp4" type="video/mp4" controls)

在那一刻,这开始起作用了。一旦我加载了页面,promise就会执行并开始下载torrent。一旦来自torrent的第一位数据通过.on上的流(“打开”)发送出去,视频就会开始播放。播放器显示,随着更多的信息被下载,更多的信息被添加到视频中,但它永远不会播放超过发送的初始数据。有没有什么方法可以告诉玩家更多的数据即将到来,而不是在读取完发送的初始数据块后立即中断?任何建议都是有帮助的,并提前感谢你!

EN

回答 1

Stack Overflow用户

发布于 2016-11-24 00:47:35

你可以试试这个:

代码语言:javascript
运行
复制
const path = require('path');
const parseRange = require('range-parser');
const engine = torrentStream('magnet:?xt=urn:btih:11a2ac68a11634e980f265cb1433c599d017a759');
const getTorrentFile = new Promise(function (resolve, reject) {
  engine.on('ready', function() {
    engine.files.forEach(function (file, idx) {
      const ext = path.extname(file.name).slice(1);
      if (ext === 'mkv' || ext === 'mp4') {
        file.ext = ext;
        resolve(file);
      }
    });
  });
});

app.use('*', function (req, res) {
  if (req.url != '/Guardians.of.the.Galaxy.2014.1080p.BluRay.x264.YIFY.mp4') {
    res.setHeader('Content-Type', 'text/html');
    if (req.method !== 'GET') return res.end();
    var rpath = __dirname + '/views/index.pug';
    fs.readFile(rpath, 'utf8', function (err, str) {
      var fn = pug.compile(str, { filename: rpath, pretty: true});
      res.end(fn());
    });
  } else {
    res.setHeader('Accept-Ranges', 'bytes');
    getTorrentFile.then(function (file) {
      res.setHeader('Content-Length', file.length);
      res.setHeader('Content-Type', `video/${file.ext}`);
      const ranges = parseRange(file.length, req.headers.range, { combine: true });
      if (ranges === -1) {
        // 416 Requested Range Not Satisfiable
        res.statusCode = 416;
        return res.end();
      } else if (ranges === -2 || ranges.type !== 'bytes' || ranges.length > 1) {
        // 200 OK requested range malformed or multiple ranges requested, stream entire video
        if (req.method !== 'GET') return res.end();
        return file.createReadStream().pipe(res);
      } else {
        // 206 Partial Content valid range requested
        const range = ranges[0];
        res.statusCode = 206;
        res.setHeader('Content-Length', 1 + range.end - range.start);
        res.setHeader('Content-Range', `bytes ${range.start}-${range.end}/${file.length}`);
        if (req.method !== 'GET') return res.end();
        return file.createReadStream(range).pipe(res);
      }
    }).catch(function (e) {
      console.error(e);
      res.end(e);
    });
  }
});

我使用range-parser来获取请求的字节范围,因为它经过了彻底的测试,可以处理奇怪的边缘情况。当服务器启动时,我也会立即启动torrent下载。这是因为浏览器经常会对同一个音频/视频文件发出多个请求,只是字节范围不同,所以在每个请求上启动一个新的torrent流并不是很有效。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40765376

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档