Ben*_*eri 4 ffmpeg stream node.js
我正在使用 FFMPEG 和 nodejs 流Duplex
类从相机创建视频流。
this.ffmpegProcess = spawn('"ffmpeg"', [
'-i', '-',
'-loglevel', 'info',
/**
* MJPEG Stream
*/
'-map', '0:v',
'-c:v', 'mjpeg',
'-thread_type', 'frame', // suggested for performance on StackOverflow.
'-q:v', '20', // force quality of image. 2-31 when 2=best, 31=worst
'-r', '25', // force framerate
'-f', 'mjpeg',
`-`,
], {
shell: true,
detached: false,
});
Run Code Online (Sandbox Code Playgroud)
在本地网络上,我们正在使用几台计算机对其进行测试,并且每件事都非常稳定,延迟最长为 2 秒。
但是,我们已将该服务导入到 AWS 生产中,当我们连接第一台计算机时,我们有大约 2 秒的延迟,但是如果另一个客户端连接到流,它们都开始延迟很多,延迟增加到 10+秒,此外视频非常慢,就像帧之间的运动一样。
现在我问 TCP 或 UDP 是因为我们使用 TCP 作为流,这意味着发送的每个数据包都在实现 TCP syn-ack-synack 协议和序列。
我的问题是,TCP 真的会导致延迟高达 10 秒且动作非常慢的问题吗?因为在本地网络上它工作得很好。
是的,TCP 绝对不是正确的协议。可以使用,但在源端进行一些修改。遗憾的是,UDP 不是灵丹妙药,如果没有额外的逻辑,UDP 也无法解决问题(如果您不关心会看到从其他帧中随机构造的损坏帧)。
TCP 的主要特点是数据包按正确的顺序传递,所有数据包都被传递。这两个功能非常有用,但对视频流非常有害。
在本地网络上,带宽相当大,而且丢包率非常低,因此 TCP 工作正常。互联网带宽是有限的,每次达到限制都会导致数据包丢失。TCP 将通过重新发送数据包来处理数据包丢失。每次重新发送都会导致流的延迟延长。
为了更好地理解,尝试想象一个数据包包含整个帧(JPEG 图像)。让我们假设链路的正常延迟是 100 毫秒(帧传输的时间)。对于 25 FPS,您需要每 40 毫秒传送一次每一帧。如果帧在传输过程中丢失,TCP 将确保副本将重新发送。TCP 可以检测到这种情况并在 2 倍延迟 - 2 * 100ms 的理想情况下进行修复(实际上它会更多,但为了简单起见,我保留了这个)。因此,在图像丢失期间,5 帧正在接收器队列中等待并等待单个丢失的帧。在本例中,一帧丢失导致 5 帧延迟。并且因为 TCP 创建了数据包队列。延迟永远不会减少,只会增长。在理想情况下,当带宽足够时,延迟仍将保持不变。
我在 nodejs 中所做的是修复源端。TCP中的数据包只有源会做才能跳过,TCP本身没有办法做。
为此,我使用了 event drain。该算法背后的想法是 ffmpeg 以自己的速度生成帧。并且 node.js 读取帧并始终保留最后接收到的帧。它还具有单帧大小的输出缓冲区。因此,如果单帧的发送由于网络条件而延迟,则来自 ffmpeg 的传入图像将被静默丢弃(这可以补偿低带宽),除了最后接收的图像。所以当 TCP 缓冲信号(通过drain
事件)某些帧已正确发送时,nodejs 将获取最后接收到的图像并将其写入流。
该算法自我调节。如果发送带宽足够(发送速度比 ffmpeg 生成的帧快),则不会丢弃任何数据包,并且将传送 25fps。如果带宽只能平均传输一半的帧,则两帧中的一帧将被丢弃,因此接收器将设置 12.5fps 但不会增加延迟。
该算法中最复杂的部分可能是正确地将字节流切片为帧。
归档时间: |
|
查看次数: |
1126 次 |
最近记录: |