我真的很难理解使用node.js将ffmpeg的实时输出流式传输到HTML5客户机的最佳方式,因为有很多变量在起作用,而且我在这个领域没有太多经验,因为我花了很多时间try 不同的组合.
我的用例是:
1) IP摄像机RTSP H.264流由FFMPEG拾取,并使用 node 中的以下FFMPEG设置重新插入mp4容器,输出到STDOUT.这只在初始客户端连接上运行,因此部分内容请求不会再次try 生成FFMPEG.
liveFFMPEG = child_process.spawn("ffmpeg", [
"-i", "rtsp://admin:12345@192.168.1.234:554" , "-vcodec", "copy", "-f",
"mp4", "-reset_timestamps", "1", "-movflags", "frag_keyframe+empty_moov",
"-" // output to stdout
], {detached: false});
2)我使用 node http服务器捕获STDOUT,并在客户端请求时将其流回客户端.当客户端第一次连接时,我生成上面的FFMPEG命令行,然后通过管道将STDOUT流传输到HTTP响应.
liveFFMPEG.stdout.pipe(resp);
我还使用了stream事件将FFMPEG数据写入HTTP响应,但没有任何区别
xliveFFMPEG.stdout.on("data",function(data) {
resp.write(data);
}
我使用以下HTTP标头(在流式传输预先录制的文件时也会使用并运行该标头)
var total = 999999999 // fake a large file
var partialstart = 0
var partialend = total - 1
if (range !== undefined) {
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; // fake a large file if no range reques
var chunksize = (end-start)+1;
resp.writeHead(206, {
'Transfer-Encoding': 'chunked'
, 'Content-Type': 'video/mp4'
, 'Content-Length': chunksize // large size to fake a file
, 'Accept-Ranges': 'bytes ' + start + "-" + end + "/" + total
});
3) 客户端必须使用HTML5视频标签.
向HTML5客户端流式播放(使用带有206 HTTP部分内容的fs.createReadStream)之前使用上述FFMPEG命令行录制的视频文件(但保存为文件而不是STDOUT)没有问题,因此我知道FFMPEG流是正确的,甚至可以在连接到HTTP node 服务器时在VLC中正确地看到视频直播.
然而,试图通过 node HTTP从FFMPEG实时流媒体似乎要困难得多,因为客户端将显示一帧,然后停止.我怀疑问题在于我没有将HTTP连接设置为与HTML5视频客户端兼容.我try 过很多方法,比如使用HTTP 206(部分内容)和200个响应,将数据放入缓冲区,然后进行流式处理,但运气不佳,所以我需要回到第一原则,以确保我的设置正确.
这是我对此的理解,如果我错了,请纠正我:
1) FFMPEG应设置为对输出进行分段,并使用空moov(FFMPEG frag_关键帧和空moov mov标志).这意味着客户端不使用moov-atom,它通常位于文件的末尾,在流媒体传输时不相关(文件没有结尾),但这意味着不可能寻找适合我的用例的对象.
2) 尽管我使用MP4片段和空MOOV,但我仍然必须使用HTTP部分内容,因为HTML5播放器将等待整个流下载后再播放,而使用实时流永远不会结束,因此不可行.
3) 我不明白为什么在流媒体直播时,将STDOUT流传输到HTTP响应的管道还不起作用.如果我保存到一个文件中,我可以使用类似的代码轻松地将该文件流传输到HTML5客户端.也许这是一个时间问题,因为FFMPEG生成需要一秒钟的时间才能启动,连接到IP摄像头并将块发送到 node ,而且 node 数据事件也不规则.然而,字节流应该与保存到文件完全相同,HTTP应该能够应对延迟.
4)当从摄像机流式传输由FFMPEG创建的MP4文件时,当从HTTP客户端判断网络日志(log)时,我看到有3个客户端请求:对视频的通用GET请求(HTTP服务器返回约40KB),然后是文件最后10K的字节范围的部分内容请求,然后是对中间位的最终请求未加载.也许HTML5客户端在收到第一个响应后会请求文件的最后一部分来加载MP4MOOV原子?如果是这种情况,它将不适用于流,因为没有MOOV文件,也没有文件结尾.
5)在try 实时流式传输时判断网络日志(log)时,我收到一个仅收到约200字节的中止的初始请求,然后重新请求再次中止,收到的200字节和第三个请求的长度仅为2K.我不明白为什么HTML5客户端会中止请求,因为字节流与我从录制的文件流式传输时可以成功使用的字节流完全相同. node 似乎也没有将ffmpeg流的睡觉发送到客户机,但是我可以在.on事件 routine 中看到ffmpeg数据,所以它会到达ffmpeg node HTTP服务器.
6) 虽然我认为将标准输出流传输到HTTP响应缓冲区应该可以工作,但我是否必须构建一个中间缓冲区和流,以允许HTTP部分内容客户端请求像(成功)读取文件时那样正常工作?我认为这是我的问题的主要原因,但是我不确定如何最好地设置它.我不知道如何处理客户端对文件末尾数据的请求,因为没有文件结尾.
7) 我在try 处理206个部分内容请求时,是不是走错了方向,这是否应该适用于正常的200个HTTP响应?HTTP 200响应对VLC很好,所以我怀疑HTML5视频客户端只能处理部分内容请求?
由于我仍在学习这方面的知识,很难解决这个问题的各个层面(FFMPEG、 node 、流媒体、HTTP、HTML5视频),所以任何指点都会非常感激.我花了数小时研究这个网站和网络,我还没有遇到任何人能够在node中进行实时流媒体,但我不能是第一个,我认为这应该能够工作(不知怎么回事!).