当前位置: 首页 > 知识库问答 >
问题:

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

卫飞
2023-03-14

我真的很难理解使用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视频客户端兼容。我尝试了很多方法,比如使用HTTP206(部分内容)和200响应,将数据放入缓冲区,然后流式传输,但没有运气,所以我需要回到第一原则,以确保我的设置是正确的。

以下是我对这应该如何工作的理解,如果我错了请纠正我:

1)FFMPEG应该设置为分割输出并使用空moov(FFMPEG frag_keyframe和empty_moov mov标志)。这意味着客户端不使用moov原子,该原子通常位于文件末尾,在流式传输时不相关(没有文件末尾),但意味着不可能查找,这对我的用例很好。

2)即使我使用MP4片段和空的MOOV,我仍然必须使用HTTP部分内容,因为HTML5播放器会等到整个流下载完后再播放,这对于一个直播流来说是永远不会结束的,所以是不可行的。

3)我不明白为什么管道STDOUT流到HTTP响应在流式传输时不起作用,如果我保存到一个文件,我可以使用类似的代码轻松地将该文件流式传输到HTML5客户端。也许这是一个定时问题,因为FFMPEG产卵启动、连接到IP摄像机并发送块到节点需要一秒钟,并且节点数据事件也是不规则的。然而,字节流应该与保存到一个文件完全相同,并且HTTP应该能够满足延迟。

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

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

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

7)我在尝试处理206个部分内容请求时是不是走错了路,这应该与正常的200个HTTP响应一起工作吗?HTTP 200响应对VLC很好,所以我怀疑HTML5视频客户端将只与部分内容请求一起工作?

由于我还在学习这些东西,它很难通过这个问题的各个层次(FFMPEG、节点、流、HTTP、HTML5视频)工作,所以任何指针都将非常感谢。我花了几个小时的时间在这个网站和网络上研究,我没有遇到过任何人能够在node中做实时流,但我不可能是第一个,我认为这应该能够工作(不知怎么的!)。

共有2个答案

曹子平
2023-03-14

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

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

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

定时可能是一个问题,在我的解决方案中,由于重新复用的组合(实际上FFMPEG必须接收实时流,重新复用它,然后将它发送到通过HTTP提供服务的节点),导致了2到6秒之间的延迟。在这方面做不到太多,但在Chrome的视频确实尽力赶上它所能做到的,这使得视频有点跳跃,但比IE11(我的首选客户端)更有时代感。

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

我还没有找到这个用例的类似例子,所以我希望上面的解释和代码能帮助其他人,特别是因为我已经从这个站点学到了这么多,但仍然认为自己是一个初学者!

虽然这是对我的具体问题的回答,但我还是选择了Szatmary的回答作为被接受的,因为它是最全面的。

高恺
2023-03-14

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

编辑2:正如评论中的人所指出的,事情发生了变化。几乎所有的浏览器都将支持AVC/AAC编解码器。iOS仍然需要HLS。但是通过像HLS.js这样的适配器,您可以在MSE中播放HLS。新的答案是hls+hls.js如果你需要iOS的话。如果你不喜欢的话,也可以使用碎片MP4(如破折号)

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

首先,我要重申:官方没有对HTML5上的直播流的支持。有黑客,但您的里程可能会有所不同。

编辑:自从我写这个答案,媒体源扩展已经成熟,现在非常接近成为一个可行的选择。它们在大多数主要浏览器上都得到支持。IOS仍然是一个顽固的问题。

接下来,您需要理解视频点播(VOD)和视频直播是非常不同的。是的,它们都是视频,但问题不同,因此格式也不同。例如,如果你的计算机中的时钟运行比它应该的快1%,你将不会注意到一个视频点播。使用现场视频,您将尝试在视频发生之前播放视频。如果您想加入一个正在进行的实时视频流,您需要初始化解码器所必需的数据,因此它必须在流中重复,或在带外发送。通过视频点播,您可以读到他们寻求的文件的开头,直到您希望的任何一点。

现在让我们深入一点。

平台:

  • IOS
  • PC
  • MAC
  • 安卓

编解码器:

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

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

  • 破折号(HTTP)
  • HLS(HTTP)
  • 闪存(RTMP)
  • 闪存(HDS)

浏览器中视频点播常用的传递方式:

  • 破折号(HTTP流式传输)
  • HLS(HTTP流式传输)
  • 闪存(RTMP)
  • Flash(HTTP流式传输)
  • MP4(HTTP伪流)
  • 我不打算谈论MKV和OOG,因为我不太了解它们。

html5视频标记:

  • MP4
  • WebM
  • Ogg

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

狩猎:

  • HLS(仅限于iOS和mac)
  • H.264
  • MP4

火狐

  • 破折号(通过MSE但没有H.264)
  • 仅通过Flash进行H.264!
  • VP9
  • MP4
  • Ogg
  • WebM

  • 闪存
  • 破折号(仅通过MSE IE 11+)
  • H.264
  • MP4

  • 闪存
  • 破折号(通过MSE)
  • H.264
  • VP9
  • MP4
  • WebM
  • Ogg

MP4不能用于视频直播(注意:DASH是MP4的超集,所以不要与之混淆)。MP4被分成两块:moov和MDAT。mdat包含原始音频视频数据。但是它没有索引,所以没有moov,它是没用的。moov包含MDAT中所有数据的索引。但由于它的格式,它不能‘平坦’直到每个帧的时间戳和大小已知。它可以构造一个moov来‘扭曲’帧大小,但这是非常浪费带宽。

所以如果你想要到处递送,我们需要找到最小的公分母。如果不使用flash示例,您将看到这里没有LCD:

  • iOS仅支持H.264视频。而且它只支持Live的HLS。
  • 火狐根本不支持H.264,除非您使用Flash
  • 闪存在IOS中不工作

与LCD最接近的是使用HLS获得iOS用户,并为其他用户提供flash。我个人最喜欢的是对HLS进行编码,然后用flash为其他人播放HLS。你可以通过JW Player6在flash中播放HLS(或者像我一样在AS3中将自己的HLS写入FLV)

很快,最常见的方法将是iOS/Mac上的HLS和其他任何地方通过MSE的DASH(这也是Netflix即将要做的)。但我们还在等待大家升级自己的浏览器。你可能还需要一个单独的DASH/VP9用于Firefox(我知道Open264;它太烂了。它不能做主视频或高姿态视频。所以目前它是没用的)。

 类似资料:
  • 问题内容: 我真的很想了解使用node.js将ffmpeg实时输出流到HTML5客户端的最佳方法,因为有很多变量在起作用,而且我在这个领域没有很多经验,花了很多小时尝试不同的组合。 我的用例是: 1)IP摄像机RTSP H.264流由FFMPEG采集,并使用节点中的以下FFMPEG设置重新混合到mp4容器中,并输出到STDOUT。这仅在初始客户端连接上运行,因此部分内容请求不会尝试再次产生FFMP

  • 我很难理解使用Node.js将ffmpeg的实时输出流到HTML5客户机的最佳方法,因为有许多变量在起作用,而且我在这方面没有太多经验,已经花了很多时间尝试不同的组合。 我的用例是: 1)IP视频摄像机RTSP H.264流由FFMPEG拾取,并使用node中的以下FFMPEG设置重新组合到mp4容器中,输出到stdout。这只在初始客户端连接上运行,这样部分内容请求就不会再次尝试生成FFMPEG

  • 我有一个现有的应用程序,通过RTMP将实时音频从Flash客户端流到Wowza zerver。。。我能够将Flash客户端连接到该设备,并获得实时、低延迟的音频。 我想在PC、Android和iOS中连接一些基于HTML5的客户端,而不在客户端使用任何Flash。。。RTMP URI通常为“rtmp://myserver/live/mystream“我尝试从HTML5页面连接视频和音频标签,但没有

  • 问题内容: 我有第三方WSDL,我需要用Java编写代码以使Web服务客户端调用第三方WSDL中的操作。现在,我已经使用Axis的WSDL2JAVA工具生成了客户端存根,并使用XMLbeans进行了数据绑定。 进行此JAVA的最佳方法是什么? 我阅读了有关SAAJ的文章,看起来这种方法会更细化吗? 除了使用WSDL2Java工具之外,还有什么其他方法可以生成代码。也许wsimport还有其他选择。

  • 问题内容: 这是我正在从事的项目的一部分。我有两个桌面Java应用程序,一个运行在服务器上(具有真实IP),另一个运行在客户端上。我只想从连接到服务器应用程序的网络摄像头流式传输实时视频,然后在客户端应用程序上播放它。我想从多个摄像机进行流式传输。 我一直在寻找Xuggler,JMF,Red5,VLCj之间的日子。我只是不知道应该从哪里开始,因为我刚接触编程中的媒体。 我从哪里开始有什么想法? 提

  • 我有一个blob数组(实际上是二进制数据--我可以表达它是最有效的。我现在使用Blobs,但可能或其他更好的方法)。每个Blob包含1秒的音频/视频数据。每秒都会生成一个新的Blob并将其追加到我的数组中。因此代码大致如下所示: 我的目标是将此音频/视频数据流式传输到HTML5元素。我知道Blob URL可以像下面这样生成和播放: