我真的很想了解使用node.js将ffmpeg实时输出流到HTML5客户端的最佳方法,因为有很多变量在起作用,而且我在这个领域没有很多经验,花了很多小时尝试不同的组合。
我的用例是:
1)IP摄像机RTSP H.264流由FFMPEG采集,并使用节点中的以下FFMPEG设置重新混合到mp4容器中,并输出到STDOUT。这仅在初始客户端连接上运行,因此部分内容请求不会尝试再次产生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)我使用节点http服务器捕获STDOUT,并根据客户端请求将其流式传输回客户端。当客户端第一次连接时,我产生了上面的FFMPEG命令行,然后将STDOUT流通过管道传递到HTTP响应。
liveFFMPEG.stdout.pipe(resp);
我还使用了流事件将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视频标签。
我没有流传输回放的问题(使用fs.createReadStream和206 HTTP部分内容)到HTML5客户端,该视频文件以前是使用上述FFMPEG命令行记录的(但保存到文件而不是STDOUT),所以我知道FFMPEG流是正确的,甚至在连接到HTTP节点服务器时,我什至可以在VLC中正确看到视频直播。
但是,尝试通过节点HTTP从FFMPEG进行实时流传输似乎要困难得多,因为客户端将显示一帧然后停止。我怀疑问题是我没有将HTTP连接设置为与HTML5视频客户端兼容。我已经尝试了多种方法,例如使用HTTP 206(部分内容)和200个响应,将数据放入缓冲区中,然后在没有运气的情况下进行流式传输,因此我需要回到第一个原则以确保我将其设置正确方式。
这是我对这应该如何工作的理解,如果我错了,请纠正我:
1)应该设置FFMPEG来分割输出并使用一个空的moov(FFMPEG frag_keyframe和empty_moov mov标志)。这意味着客户端不会使用moov原子,该原子通常位于文件的末尾,而流在传输时是不相关的(文件的末尾),但是这意味着没有可能的查找,这对我的用例来说很好。
2)即使我使用MP4片段和空的MOOV,我仍然必须使用HTTP部分内容,因为HTML5播放器将等到整个流下载完毕后再播放,而实时流永远不会结束,因此无法使用。
3)我不明白为什么在实时流式传输时将STDOUT流传输到HTTP响应为什么仍然不起作用,如果我保存到文件中,则可以使用类似的代码轻松地将此文件流式传输到HTML5客户端。也许这是一个定时问题,因为FFMPEG生成需要一秒钟才能启动,连接到IP摄像机并将块发送到节点,并且节点数据事件也是不规则的。但是,字节流应与保存到文件完全相同,并且HTTP应该能够应付延迟。
4)当从相机流式传输FFMPEG创建的MP4文件时,从HTTP客户端检查网络日志时,我看到有3个客户端请求:对视频的常规GET请求,HTTP服务器返回大约40Kb,然后是部分内容请求,其字节范围为文件的最后10K,然后最终请求为中间的位未加载。也许HTML5客户端一旦收到第一个响应,便要求文件的最后部分加载MP4 MOOV原子?如果是这种情况,将无法进行流式传输,因为没有MOOV文件,也没有文件结尾。
5)在尝试实时流式传输时检查网络日志时,我收到了一个中止的初始请求,仅收到了200个字节,然后再次发出了200字节的中止请求,而第三个请求的长度只有2K。我不明白为什么HTML5客户端会中止请求,因为字节流与从记录文件流式传输时可以成功使用的字节流完全相同。似乎节点也没有将其余的FFMPEG流发送到客户端,但是我可以在.on事件例程中看到FFMPEG数据,因此它正在到达FFMPEG节点HTTP服务器。
6)尽管我认为将STDOUT流传输到HTTP响应缓冲区应该可以,但是我是否必须构建一个中间缓冲区和流,以使HTTP部分内容客户端请求能够像成功(成功)读取文件时一样正常工作?我认为这是我遇到问题的主要原因,但是我不确定在Node中如何最好地进行设置。而且我不知道如何在文件末尾处理客户端对数据的请求,因为没有文件末尾。
7)我是否在尝试处理206个部分内容请求时走错了轨道,并且这是否可以正常处理200个HTTP响应?HTTP 200响应对于VLC可以正常工作,因此我怀疑HTML5视频客户端仅适用于部分内容请求吗?
由于我仍在学习这些东西,因此很难解决该问题的各个层次(FFMPEG,节点,流,HTTP,HTML5视频),因此任何指针将不胜感激。我已经花了数小时在此站点和网络上进行研究,但我还没有遇到能够在节点中进行实时流式传输的任何人,但我不能成为第一个,而且我认为这应该可以工作(以某种方式!)。
Content-Type
在你的头上?您是否在使用块编码?那就是我要开始的地方。另外,HTML5不一定提供流功能,您可以在此处阅读更多内容。您很可能需要实现一种使用自己的方式来缓冲和播放视频流的方法(请参见此处),以为可能无法很好地支持此方法。也谷歌到MediaSource API。