我真的被困住了,试图理解使用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视频),所以任何指针将非常感谢。我花了几个小时在这个网站和网络上研究,我还没有遇到任何人能够在节点中做实时流,但我不能是第一个,我认为这应该能够工作(以某种方式!)。


当前回答

谢谢大家,尤其是szatmary,因为这是一个复杂的问题,有很多层次,所有这些都必须在你可以直播视频之前工作。澄清我最初的问题,HTML5视频使用vs flash——我的用例对HTML5有强烈的偏好,因为它是通用的,易于在客户端和未来实现。Flash是第二好的,所以让我们继续用HTML5来回答这个问题。

I learnt a lot through this exercise and agree live streaming is much harder than VOD (which works well with HTML5 video). But I did get this to work satisfactorily for my use case and the solution worked out to be very simple, after chasing down more complex options like MSE, flash, elaborate buffering schemes in Node. The problem was that FFMPEG was corrupting the fragmented MP4 and I had to tune the FFMPEG parameters, and the standard node stream pipe redirection over http that I used originally was all that was needed.

在MP4中,有一个“碎片”选项,将MP4分解成更小的片段,这些片段有自己的索引,并使MP4直播选项可行。但是不可能返回到流中(对我的用例来说没问题),并且FFMPEG的后续版本支持碎片。

注意,时间可能是一个问题,在我的解决方案中,由于重新配置的组合导致了2到6秒的延迟(实际上FFMPEG必须接收实时流,重新配置然后将其发送到节点以通过HTTP提供服务)。对此我们无能为力,然而在Chrome中,视频确实试图尽可能多地赶上它所能做的,这使得视频有点跳跃,但比IE11(我的首选客户端)更与时俱进。

在这篇文章中,我们不解释代码是如何工作的,而是查看带有注释的GIST(客户端代码不包括在内,它是一个带有节点http服务器地址的标准HTML5视频标记)。GIST在这里:https://gist.github.com/deandob/9240090

我还没有找到这个用例的类似例子,所以我希望上面的解释和代码能帮助到其他人,尤其是我已经从这个网站学到了很多东西,而且我仍然认为自己是一个初学者!

虽然这是我具体问题的答案,但我选择了szatmary的答案作为公认的答案,因为它是最全面的。

其他回答

看一看JSMPEG项目。这里实现了一个很棒的想法——使用JavaScript在浏览器中解码MPEG。例如,来自编码器(FFMPEG)的字节可以使用WebSockets或Flash传输到浏览器。如果社区能够迎头赶上,我认为这将是目前最好的HTML5直播视频流解决方案。

这是一个非常普遍的误解。没有实时HTML5视频支持(除了iOS和Mac Safari上的HLS)。你可以使用webm容器来“破解”它,但我不期望它能得到普遍支持。您正在寻找的内容包含在媒体源扩展中,在那里您可以一次将片段提供给浏览器。但是您需要编写一些客户端javascript。

看看这个解决方案。 据我所知,Flashphoner允许在纯HTML5页面中播放实时音频+视频流。

他们使用MPEG1和G.711编解码器进行回放。 黑客正在将解码的视频渲染到HTML5画布元素,并通过HTML5音频上下文播放解码的音频。

如何使用jpeg解决方案,只是让服务器分发jpeg一个接一个的浏览器,然后使用画布元素绘制这些jpeg ? http://thejackalofjavascript.com/rpi-live-streaming/

我写了一个HTML5视频播放器在百老汇h264编解码器(emscripten),可以播放实时(没有延迟)h264视频在所有浏览器(桌面,iOS,…)。

视频流通过websocket发送到客户端,逐帧解码并显示在画布中(使用webgl加速)

在github上查看https://github.com/131/h264-live-player。