首页 > 解决方案 > TCP还是UDP?视频流的制作延迟

问题描述

我正在使用 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,
});

在本地网络上,我们正在使用几台计算机对其进行测试,并且每件事都非常稳定,延迟时间最长为 2 秒。

但是,我们已经将该服务导入 AWS 生产环境,当我们连接第一台计算机时,我们有大约 2 秒的延迟,但是如果另一个客户端连接到流,它们都开始延迟很多,延迟累积到 10+秒,此外视频非常慢,就像帧之间的运动一样。

现在我问 TCP 还是 UDP 是因为我们使用 TCP 作为流,这意味着发送的每个数据包都实现了 TCP syn-ack-synack 协议和序列。

我的问题是,TCP 真的会导致延迟达到 10 秒且动作非常慢的问题吗?因为在本地网络上它工作得很好。

标签: node.jsffmpegstream

解决方案


是的,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 但不会增加延迟。

该算法中最复杂的部分可能是将字节流正确地分割成帧。


推荐阅读