NodeJS - 如何将同一视频流传输到多个客户端?

时间:2013-08-29 14:51:25

标签: html5 node.js video ffmpeg multicast

我们遇到了尝试投放视频流的情况。

由于HTML5视频标记不支持udp进行多播,因此我们尝试重新使用已转换的ffmpeg流并将其发送给多个响应。但这不起作用。

第一个响应使流正常,但第二个响应没有。 似乎流不能被传递给另一个响应,也不能被克隆。

之前有人这样做过吗?有什么想法吗?

提前致谢!

以下是代码:

var request = require('request');
var http = require('http');
var child_process = require("child_process");
var n = 1;
var stdouts = {};

http.createServer(function (req, resp) {

console.log("***** url ["+req.url+"], call "+n);

if (req.url != "/favicon.ico" && req.url != "/")
{
var params = req.url.substring(1).split("/");

switch (params[0])
{
  case "VIEW":
    if (params[1] == "C2FLOOR1" || params[1] == "C2FLOOR2" || params[1] == "C2PORFUN" || params[1] == "C2TESTCAM")
      var camera = "rtsp://192.168.16.19:554/Inter/Cameras/Stream?Camera="+params[1];
    else
      var camera = "http://192.168.16.19:8609/Inter/Cameras/GetStream?Camera="+params[1];

    // Write header
    resp.writeHead(200, {'Content-Type': 'video/ogg', 'Connection': 'keep-alive'});

    if (stdouts.hasOwnProperty(params[1]))
    {
      console.log("Getting stream already created for camera "+params[1]);

      var newStdout = Object.create(stdouts[params[1]]);

      newStdout.pipe(resp);
    }
    else
    {
        // Start ffmpeg
        var ffmpeg = child_process.spawn("ffmpeg",[
        "-i",camera,
        "-vcodec","libtheora",
        "-qscale:v","7",        // video quality
        "-f","ogg",             // File format
        "-g","1",               // GOP (Group Of Pictures) size
        "-"                     // Output to STDOUT
        ]);

        stdouts[params[1]] = ffmpeg.stdout;

        // Pipe the video output to the client response
        ffmpeg.stdout.pipe(resp);

    console.log("Initializing camera at "+camera);
    }

    // Kill the subprocesses when client disconnects
/*
    resp.on("close",function(){
      ffmpegs[params[1]].kill();
      console.log("FIM!");
    });
*/
    break;
}
}
else
{
resp.writeHeader(200, {"Content-Type": "text/html"});
resp.write("WRONG CALL");
resp.end();
}
n++;

}).listen(8088);

console.log('Server running at port 8088');

1 个答案:

答案 0 :(得分:2)

Streams可以被认为是固定大小的queues,因为他们在拒绝接受更多数据或数据开始脱落之前“缓冲”或存储某些预先定义的数据量流的阅读结束;它们可以被视为队列,因为它们以“先进先出”(FIFO)顺序存储数据。

写入流可使所有流的当前读取器(已打开流以供读取的实体)使用数据。

从流中读取会从流的一端删除固定数量的数据,从而释放另一端的空间以可能接受更多数据。

从流中读取数据并将更多数据添加到流中以填充其缓冲区后,已读取的数据将消失。

Node.js流确实可以同时拥有多个读者 ,每个读者都有自己的指向流缓冲区的指针,指示它消耗了多少数据,但是如果你要添加一个新的读取器在流已经将数据刷出缓冲区后,这些数据将不再可用。

我相信当后续请求尝试从流中读取时,您看到的浏览器超时正在发生,因为流已经耗尽了数据,并且进一步尝试从流中读取数据等待新数据可用。

从本质上讲,您似乎正在尝试使用流,就像它们是长期存在的数据缓存一样,它们不是。

您可以尝试增加流的“高水位线”,这样它们就可以在内存中缓冲整个视频流,这样后续读者就可以从内存中读取整个数据流而不会耗尽流的缓冲区。 (有关如何增加流的缓冲区大小,请参阅节点的stream文档。)

但是,我怀疑你会看到通过增加操作系统的I / O缓冲区大小或从内存或固态硬盘驱动器读取数据来做出很多改进。

更新(每条评论)

如果您需要随着时间的推移向多个观看者提供一个或多个连续(实时)视频供稿,您是否考虑过以“流动”模式创建和初始化视频数据流,并依赖他们发出的事件向用户提供数据?

我想到

  • 为全局上下文中的每个实时订阅源创建流(以确保流将在请求之间保持)以及“流动”(或事件驱动)模式
  • 将新流分配到唯一feeds
  • 的全局feedName
  • 设置Stream.end事件监听器,以便在Feed意外结束时重新初始化

然后在HTTP请求处理程序

  • feeds
  • feedName找到请求的Feed
  • 在其上注册事件侦听器
    1. Stream.readable - 确保有数据可供阅读
    2. Stream.data - 将数据传输到响应
    3. Stream.endhttp.ServerResponse.close - 优雅地取消注册前两个处理程序

我还设置了一个计时器来取消注册请求处理程序中设置的所有事件,如果在一段合理的时间内没有来自流的数据,则向客户端发送适当的错误。

(“flow”模式在node.js Stream文档中有描述。)