我真的被困住了,试图理解使用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。creatererestream with 206 HTTP部分内容)到HTML5客户端之前用上面的FFMPEG命令行录制的视频文件(但保存到文件而不是STDOUT),所以我知道FFMPEG流是正确的,我甚至可以正确地看到VLC中的视频直播时连接到HTTP节点服务器。
然而,试图通过节点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) When checking the network log from the HTTP client when streaming a MP4 file created by FFMPEG from the camera, I see there are 3 client requests: A general GET request for the video, which the HTTP server returns about 40Kb, then a partial content request with a byte range for the last 10K of the file, then a final request for the bits in the middle not loaded. Maybe the HTML5 client once it receives the first response is asking for the last part of the file to load the MP4 MOOV atom? If this is the case it won't work for streaming as there is no MOOV file and no end of the file.
5) When checking the network log when trying to stream live, I get an aborted initial request with only about 200 bytes received, then a re-request again aborted with 200 bytes and a third request which is only 2K long. I don't understand why the HTML5 client would abort the request as the bytestream is exactly the same as I can successfully use when streaming from a recorded file. It also seems node isn't sending the rest of the FFMPEG stream to the client, yet I can see the FFMPEG data in the .on event routine so it is getting to the FFMPEG node HTTP server.
6)虽然我认为管道的STDOUT流到HTTP响应缓冲区应该工作,我必须建立一个中间缓冲区和流,将允许HTTP部分内容客户端请求正常工作,就像它(成功)读取文件?我认为这是我遇到问题的主要原因,但是我不确定在Node中如何最好地设置它。我不知道如何处理客户端请求的数据在文件的末尾,因为没有结束的文件。
7)我是否在错误的轨道上试图处理206个部分内容请求,这应该与正常的200个HTTP响应一起工作?HTTP 200响应工作良好的VLC,所以我怀疑HTML5视频客户端将只与部分内容请求工作?
因为我还在学习这个东西,很难通过这个问题的各个层面(FFMPEG,节点,流媒体,HTTP, HTML5视频),所以任何指针将非常感谢。我花了几个小时在这个网站和网络上研究,我还没有遇到任何人能够在节点中做实时流,但我不能是第一个,我认为这应该能够工作(以某种方式!)。