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


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


编辑3:从IOS 10开始,HLS将支持碎片mp4文件。这个问题的答案 现在,是创建碎片化的mp4资产,与DASH和HLS清单。假设flash、iOS9及以下版本和ie10及以下版本不存在。

这条线以下的所有东西都过时了。把它留在这里,留给子孙后代。


编辑2:正如人们在评论中指出的那样,事情变了。 几乎所有浏览器都支持AVC/AAC编解码器。 iOS仍然需要HLS。但是通过像hls.js这样的适配器你可以玩 MSE中的HLS。如果你需要iOS,新的答案是HLS+ HLS .js。或者只是 碎片MP4(即DASH)如果你没有

有很多原因导致视频,特别是直播视频非常困难。(请注意,最初的问题指定HTML5视频是必需的,但提问者在评论中表示Flash是可能的。所以这个问题很容易误导人)

首先我要重申的是:官方不支持HTML5上的直播。有一些技巧,但你的效果可能会有所不同。

编辑:自从我写了这个答案,媒体源扩展已经成熟, 现在非常接近成为一个可行的选择。他们得到了支持 在大多数主流浏览器上。IOS仍然是一个坚守阵地的平台。

Next, you need to understand that Video on demand (VOD) and live video are very different. Yes, they are both video, but the problems are different, hence the formats are different. For example, if the clock in your computer runs 1% faster than it should, you will not notice on a VOD. With live video, you will be trying to play video before it happens. If you want to join a a live video stream in progress, you need the data necessary to initialize the decoder, so it must be repeated in the stream, or sent out of band. With VOD, you can read the beginning of the file them seek to whatever point you wish.

现在让我们再深入挖掘一下。

平台:

iOS 个人电脑 Mac 安卓

编解码器:

vp8/9 h . 桦木(vp3)

浏览器中常见的视频直播方式:

破折号(HTTP) HLS (HTTP) flash (RTMP) flash (HDS)

浏览器VOD常用的投放方式:

DASH (HTTP流媒体) HLS (HTTP流) flash (RTMP) flash (HTTP流) MP4 (HTTP伪流) 我不打算谈论MKV和OOG,因为我不是很了解它们。

Html5视频标签:

MP4 webm ogg


让我们看看哪些浏览器支持什么格式

Safari:

HLS(仅限iOS和mac) h . MP4

火狐

DASH(通过MSE,而不是h.264) h.264只通过Flash ! VP9 MP4 OGG Webm

IE

闪光 DASH(仅通过MSE IE 11+) h . MP4

闪光 DASH(通过MSE) h . VP9 MP4 webm ogg

MP4 cannot be used for live video (NOTE: DASH is a superset of MP4, so don't get confused with that). MP4 is broken into two pieces: moov and mdat. mdat contains the raw audio video data. But it is not indexed, so without the moov, it is useless. The moov contains an index of all data in the mdat. But due to its format, it can not be 'flattened' until the timestamps and size of EVERY frame is known. It may be possible to construct an moov that 'fibs' the frame sizes, but is is very wasteful bandwidth wise.

所以如果你想在任何地方交付,我们需要找到最小公分母。如果不使用闪光灯,你会发现这里没有液晶显示器 例子:

iOS只支持h.264视频。并且它只支持实时HLS。 Firefox根本不支持h.264,除非你使用flash Flash不能在iOS中运行

最接近LCD的是使用HLS来获取iOS用户,并为其他所有人提供flash。 我个人最喜欢的是编码HLS,然后使用flash为其他人播放HLS。你可以通过JW player 6在flash中玩HLS,(或者像我一样在AS3中写你自己的HLS到FLV)

很快,最常见的方法将是在iOS/Mac上使用HLS,在其他地方通过MSE使用DASH(这是Netflix很快要做的事情)。但我们仍在等待每个人升级他们的浏览器。你可能还需要一个单独的DASH/VP9用于Firefox(我知道open264;它糟透了。它不能在主屏幕或高屏幕上播放视频。所以目前它是无用的)。


谢谢大家,尤其是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直播视频流解决方案。


一种将基于rtsp的网络摄像头直播到HTML5客户端的方法(涉及重新编码,因此预计质量损失并需要一些cpu功耗):

Set up an icecast server (could be on the same machine you web server is on or on the machine that receives the RTSP-stream from the cam) On the machine receiving the stream from the camera, don't use FFMPEG but gstreamer. It is able to receive and decode the RTSP-stream, re-encode it and stream it to the icecast server. Example pipeline (only video, no audio): gst-launch-1.0 rtspsrc location=rtsp://192.168.1.234:554 user-id=admin user-pw=123456 ! rtph264depay ! avdec_h264 ! vp8enc threads=2 deadline=10000 ! webmmux streamable=true ! shout2send password=pass ip=<IP_OF_ICECAST_SERVER> port=12000 mount=cam.webm

然后,您可以使用<video>标签和icecast-stream的URL (http://127.0.0.1:12000/cam.webm),它将在支持webm的所有浏览器和设备中工作


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

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


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

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

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


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