实时HTTP流到HTML5视频客户端的最佳方法

我试图理解使用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视频标签。

对于以前用FFMPEG命令行(但保存到文件而不是STDOUT)中记录的视频文件,我对流式播放(使用带有206个HTTP部分内容的fs.createReadStream)没有任何问题,所以我知道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)我不明白为什么将流式传输到HTTP响应在流式传输时不起作用,但如果我保存到文件中,我可以使用类似代码轻松地将此文件传输到HTML5客户端。 也许这是一个时间问题,因为启动FFMPEG spawn需要一秒钟的时间,连接到IP摄像机并将块发送到节点,并且节点数据事件也是不规则的。 然而,字节流应该与保存到文件完全一样,并且HTTP应该能够迎合延迟。

4)当从照相机流传输由FFMPEG创建的MP4文件时,从HTTP客户端检查网络日志时,我看到有3个客户端请求:HTTP服务器返回约40Kb的视频的一般GET请求,然后是部分内容请求的文件的最后10K的字节范围,然后最后请求中间位未加载。 也许HTML5客户端收到第一个响应后会要求文件的最后部分加载MP4 MOOV原子? 如果是这种情况,它将无法用于流式传输,因为没有MOOV文件,也没有文件结束。

5)当试图流式传输时检查网络日志时,我收到一个中止的初始请求,只收到大约200个字节,然后再次请求中止200字节和第三个请求,这个请求只有2K长。 我不明白为什么HTML5客户端会中止请求,因为字节流与从录制文件流式传输时可以成功使用的字节流完全相同。 它似乎也没有将剩余的FFMPEG流发送给客户端,但我可以在.on事件例程中看到FFMPEG数据,因此它正在到达FFMPEG节点HTTP服务器。

6)尽管我认为将STDOUT流管道化到HTTP响应缓冲区应该可行,但我是否必须构建一个中间缓冲区和流,以允许HTTP部分内容客户端请求正常工作,就像它在成功读取文件时一样? 我认为这是我的问题的主要原因,但我不确定在节点如何最好地设置。 我不知道如何处理客户端文件末尾的数据请求,因为没有文件结尾。

7)我是否试图处理206个部分内容请求,并且应该使用普通的200个HTTP响应? HTTP 200响应对于VLC工作正常,所以我怀疑HTML5视频客户端只能处理部分内容请求?

由于我仍然在学习这些东西,所以很难解决这个问题的各个层面(FFMPEG,节点,流媒体,HTTP,HTML5视频),所以任何指针都将不胜感激。 我花了数小时研究这个网站和网络,我还没有遇到任何能够在节点上实时流式传输的人,但我不能成为第一个,我认为这应该能够工作(以某种方式!)。


编辑3:从IOS 10开始,HLS将支持分段的mp4文件。 现在的答案是创建碎片化的mp4资产,并带有DASH和HLS清单。 >假装闪光灯,iOS9及以下版本和IE10及以下版本不存在。

这条线下面的所有东西都过时了。 留在这里为后代。


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

视频,特别是直播视频非常困难的原因有很多。 (请注意,原始问题指定HTML5视频是一项要求,但提问者声明Flash中可能有注释,因此,这个问题很快就会引起误解)

首先,我将重申: HTML5上没有实时流式传输的官方支持 。 有黑客,但你的里程可能会有所不同。

编辑:因为我写这个答案媒体源扩展已经成熟,现在非常接近成为一个可行的选择。 它们在大多数主流浏览器上都受支持。 内部监督办公室继续坚持下去。

接下来,您需要了解视频点播(VOD)和实况视频非常不同。 是的,他们都是视频,但问题不同,因此格式不同。 例如,如果计算机中的时钟比它应该运行速度快1%,那么您将不会注意到VOD。 借助实时视频,您将尝试在视频发生之前播放视频。 如果您想要加入正在进行的实时视频流,则需要初始化解码器所需的数据,因此必须在流中重复,或者发送带外。 借助VOD,您可以阅读他们寻找文件的开头,无论您希望如何。

现在我们来深入一点。

平台:

  • iOS版
  • 个人计算机
  • 苹果电脑
  • Android的
  • 编解码器:

  • VP8 / 9
  • H.264
  • thora(vp3)
  • 浏览器中实时视频的常用传送方法:

  • DASH(HTTP)
  • HLS(HTTP)
  • 闪存(RTMP)
  • 闪存(HDS)
  • 浏览器中VOD的常见传送方式:

  • DASH(HTTP流媒体)
  • HLS(HTTP流媒体)
  • 闪存(RTMP)
  • 闪光灯(HTTP流媒体)
  • MP4(HTTP伪流媒体)
  • 我不会谈论MKV和OOG,因为我不太了解他们。
  • html5视频标签:

  • MP4
  • WEBM
  • OGG

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

    苹果浏览器:

  • HLS(仅限iOS和Mac)
  • H.264
  • MP4
  • 火狐

  • DASH(通过MSE但不是h.264)
  • h.264只能通过Flash!
  • VP9
  • MP4
  • OGG
  • WEBM
  • IE

  • DASH(仅限MSE IE 11+)
  • H.264
  • MP4
  • DASH(通过MSE)
  • H.264
  • VP9
  • MP4
  • WEBM
  • OGG
  • MP4不能用于实时视频(注:DASH是MP4的超集,所以不要与此混淆)。 MP4分为两部分:moov和mdat。 mdat包含原始音频视频数据。 但它没有索引,所以没有moov就没用了。 moov包含mdat中所有数据的索引。 但是由于其格式的原因,在每个帧的时间戳和大小已知之前,它不能被“拼合”。 有可能构建一个能够“帧化”帧大小的moov,但带宽明显是非常浪费的。

    所以,如果你想在任何地方交付,我们需要找到最不共同的分母。 没有诉诸闪光灯的例子,你会看到这里没有LCD:

  • iOS仅支持h.264视频。 它只支持HLS直播。
  • 除非您使用闪光灯,否则Firefox根本不支持h.264
  • Flash在iOS中不起作用
  • 最接近液晶显示器的是使用HLS来让你的iOS用户,并为其他人闪光。 我个人最喜欢的是编码HLS,然后使用闪光灯为其他人播放HLS。 您可以通过JW播放器6在闪存中播放HLS(或像我一样在AS3中将自己的HLS编写到FLV中)

    很快,最常见的方法就是iOS / Mac上的HLS和其他地方的MSE上的DASH(这就是Netflix即将开展的工作)。 但我们仍在等待每个人升级他们的浏览器。 你也可能需要一个单独的DASH / VP9 for Firefox(我知道关于open264;它很糟糕,它不能以主要或高级配置来播放视频,所以它目前是无用的)。


    感谢所有人,特别是szatmary,因为这是一个复杂的问题,并且有很多层次,所有这些都必须先行工作,然后才能传送实况视频。 为了澄清我最初的问题和HTML5视频的使用与Flash - 我的用例对HTML5有着强烈的偏好,因为它是通用的,易于在客户端和未来实现。 Flash是第二好的,所以让我们坚持使用HTML5来解决这个问题。

    我通过这个练习学到了很多东西,并且认同实时流媒体比VOD(它适用于HTML5视频)要困难得多。 但是我确实得到了这个令人满意的用例,并且在解决了更复杂的选项(如MSE,闪存,Node中精心设计的缓冲机制)后,解决方案变得非常简单。 问题是FFMPEG破坏了零碎的MP4并且我不得不调整FFMPEG参数,并且通过我原来使用的http的标准节点流管道重定向是所有需要的。

    在MP4中有一个'分片'选项,可以将mp4分割成更小的片段,这些片段拥有自己的索引,并使mp4直播选项可行。 但是无法回到流中(对我的用例来说OK),而FFMPEG的更高版本支持分片。

    注意时间可能是一个问题,并且对于我的解决方案,由于重新组合(由于FFMPEG必须接收实时流,将其重新发送给节点以通过HTTP进行服务) 。 关于这一点可以做的不多,但是在Chrome中,视频确实尽可能地赶上,这使得视频有点跳跃,但比IE11(我的首选客户端)更加流行。

    与其解释代码在这篇文章中的工作方式,不要说明GIST的注释(客户端代码不包括在内,它是一个带有节点http服务器地址的标准HTML5视频标签)。 GIST在这里:https://gist.github.com/deandob/9240090

    我一直无法找到这个用例的类似例子,所以我希望上面的解释和代码能够帮助其他人,特别是我从这个网站学到很多东西,并且仍然认为自己是初学者!

    虽然这是我的具体问题的答案,但我已经选择szatmary的答案作为接受的答案,因为它是最全面的答案。


    看看JSMPEG项目。 在那里实现了一个好主意 - 用JavaScript在浏览器中解码MPEG。 例如,编码器的字节(例如,FFMPEG)可以使用WebSocket或Flash传输到浏览器。 如果社区能够赶上,我认为它将成为目前最好的HTML5实时视频流媒体解决方案。

    链接地址: http://www.djcxy.com/p/44703.html

    上一篇: Best approach to real time http streaming to HTML5 video client

    下一篇: WebSockets protocol vs HTTP