3

我们有一种情况试图提供视频流。

由于 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');
4

1 回答 1

2

可以被认为是固定大小的队列,因为它们在拒绝接受更多数据或数据开始从流的读取端脱落之前“缓冲”或存储一定数量的数据;它们可以被视为队列,因为它们以“先进先出”(FIFO)的顺序存储数据。

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

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

一旦从流中读取数据并将更多数据添加到流中以填充其缓冲区,那些已读取的数据就消失了。

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

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

从本质上讲,您似乎在尝试使用流,就好像它们是长期存在的数据缓存一样,但事实并非如此。

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

但是,我怀疑通过增加操作系统 I/O 缓冲区的大小或从内存或固态硬盘驱动器读取数据,您是否会看到响应有很大改善。

更新(每条评论)

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

我会考虑

  • 在全局上下文(以确保流在请求之间持续存在)和“流动”(或事件驱动)模式下为每个实时提要创建一个流
  • 将新流分配给feeds一个唯一的全局feedName
  • Stream.end如果提要意外结束,则为其设置事件侦听器以重新初始化流

然后在 HTTP 请求处理程序中

  • feeds在以下位置找到请求的提要feedName
  • 在其上注册事件监听器
    1. Stream.readable- 确保有数据要读取
    2. Stream.data- 将数据传递给响应
    3. Stream.endhttp.ServerResponse.close- 优雅地取消注册前两个处理程序

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

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

于 2013-08-29T18:57:23.973 回答