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

时间:2014-02-20 23:13:52

标签: html5 node.js ffmpeg streaming

我真的不想尝试了解使用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);

我还使用了stream事件将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视频标签。

流式播放(使用带有206个HTTP部分内容的fs.createReadStream)到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视频),所以任何指针都会受到高度赞赏。我花了几个小时研究这个网站和网络,我没有遇到任何能够在节点上进行实时流媒体的人,但我不能成为第一个,我认为这应该能够工作(不知何故!)。

9 个答案:

答案 0 :(得分:205)

  

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

此行以下的所有内容都已过期。为后人留在这里。


  

编辑2:正如评论中的人指出的那样,事情会发生变化。   几乎所有浏览器都支持AVC / AAC编解码器。   iOS仍然需要HLS。但是通过像hls.js这样的适配器你可以玩   MSE中的HLS。如果您需要iOS,新答案是HLS + hls.js。要不就   如果您没有

,则分段MP4(即DASH)

视频,特别是直播视频非常困难的原因有很多。 (请注意,原始问题指出HTML5视频是一项要求,但提问者在评论中说明Flash是可能的。所以立即,这个问题具有误导性)

首先我要重申:没有正式支持HTML5上的直播。有黑客,但你的里程可能会有所不同。

  编辑:自从我写了这个答案后,Media Source Extensions已经成熟,   现在非常接近成为一个可行的选择。他们受到支持   在大多数主流浏览器上内部监督办公室仍然是一个坚持。

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

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

平台:

  • 的iOS
  • PC
  • 的Mac
  • 的Android

编解码器:

  • VP8 / 9
  • H.264
  • thora(vp3)

浏览器中实时视频的常用投放方式:

  • DASH(HTTP)
  • HLS(HTTP)
  • flash(RTMP)
  • flash(HDS)

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

  • DASH(HTTP Streaming)
  • HLS(HTTP Streaming)
  • flash(RTMP)
  • flash(HTTP Streaming)
  • MP4(HTTP伪流媒体)
  • 我不会谈论MKV和OOG因为我不太了解它们。

html5视频标记:

  • MP4
  • WEBM
  • OGG

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

Safari浏览器:

  • 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。帧大小,但带宽是非常浪费的。

因此,如果您想在任何地方提供服务,我们需要找到最小公分母。你会看到这里没有液晶显示器而不采用闪光灯 例如:

  • iOS仅支持h.264视频。它只支持HLS for live。
  • 除非您使用flash
  • ,否则Firefox根本不支持h.264
  • Flash无法在iOS中运行

与LCD最接近的是使用HLS来吸引iOS用户,并为其他人使用闪存。 我个人最喜欢的是对HLS进行编码,然后使用flash为其他人播放HLS。您可以通过JW播放器6在闪存中播放HLS,(或者像我一样在AS3中将您自己的HLS写入FLV)

很快,最常见的方法是在iOS / Mac上使用HLS,在其他地方通过MSE使用DASH(这就是Netflix即将推出的功能)。但我们仍在等待所有人升级他们的浏览器。您可能还需要一个单独的DASH / VP9 for Firefox(我知道open264;它很糟糕。它无法在主要或高调中播放视频。因此它目前无用)。

答案 1 :(得分:74)

谢谢大家特别是szatmary,因为这是一个复杂的问题,并且有很多层,所有这些都必须在流媒体视频流之前工作。为了澄清我的原始问题和HTML5视频使用vs flash - 我的用例非常偏爱HTML5,因为它是通用的,易于在客户端和未来实现。 Flash是第二好的,所以我们坚持使用HTML5来解决这个问题。

我通过这个练习学到了很多东西,并且同意实时流式传输比VOD(与HTML5视频效果很好)相比要困难得多。但我确实让我的用例令人满意,并且在追逐更复杂的选项如MSE,flash,精心设计的Node缓冲方案后,解决方案变得非常简单。问题是FFMPEG正在破坏碎片化的MP4,我不得不调整FFMPEG参数,而我最初使用的http标准节点流管道重定向就是所需要的。

在MP4中有一个'fragmentation'选项可以将mp4分成更小的片段,这些片段有自己的索引并使mp4实时流媒体选项可行。但是不可能回到流中(对我的用例来说没问题),以及更高版本的FFMPEG支持碎片化。

注意时间可能是个问题,而且我的解决方案由于重新组合而导致滞后2到6秒(有效的FFMPEG必须接收实时流,然后再将其发送到节点进行服务)通过HTTP)。关于这一点可以做的不多,但是在Chrome中,视频确实试图尽可能多地追赶视频,使视频有点跳跃但比IE11(我的首选客户端)更新。

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

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

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

答案 2 :(得分:13)

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

答案 3 :(得分:12)

我在broadway h264编解码器(emscripten)上编写了一个HTML5视频播放器,可以在所有浏览器(桌面,iOS,...)上播放实时(无延迟)h264视频。

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

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

答案 4 :(得分:11)

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

  • 设置一个icecast服务器(可以在你的网络服务器所在的同一台机器上,也可以在从凸轮上接收RTSP流的机器上)
  • 在从相机接收流的机器上,不要使用FFMPEG而是使用gstreamer。它能够接收和解码RTSP流,对其进行重新编码并将其流式传输到icecast服务器。示例管道(仅视频,无音频):

    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
    

=&GT;然后,您可以使用&lt; video&gt;使用icecast-stream(http://127.0.0.1:12000/cam.webm)的URL标记,它可以在支持webm的每个浏览器和设备中使用

答案 5 :(得分:3)

看看this solution。 据我所知,Flashphoner允许在纯HTML5页面中播放Live音频+视频流。

他们使用 MPEG1 G.711 编解码器进行播放。 黑客将解码视频呈现为HTML5画布元素,并通过HTML5音频上下文播放解码后的音频。

答案 6 :(得分:3)

如何使用jpeg解决方案,让服务器逐个将jpeg分发到浏览器,然后使用canvas元素绘制这些jpeg? http://thejackalofjavascript.com/rpi-live-streaming/

答案 7 :(得分:2)

这是一种非常常见的误解。没有实时HTML5视频支持(iOS和Mac Safari上的HLS除外)。您可以使用webm容器“破解”它,但我不希望它得到普遍支持。您要查找的内容包含在Media Source Extensions中,您可以将片段一次一个地提供给浏览器。但你需要写一些客户端javascript。

答案 8 :(得分:2)

尝试binaryjs。就像socket.io一样,但它唯一能做的就是流式传输音频视频。 Binaryjs google it

相关问题