node.js - TCP还是UDP?视频流的制作延迟

标签 node.js ffmpeg stream

我正在使用 FFMPEG 和 nodejs 流 Duplex 从相机创建视频流类(class)。

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 协议(protocol)和序列。

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

最佳答案

是的,TCP 绝对不是正确的协议(protocol)。可以使用,但在源端进行了一些修改。遗憾的是,UDP 不是 Elixir ,如果没有额外的逻辑,UDP 也无法解决问题(如果您不关心会看到从其他帧随机构建的损坏帧)。

解释

TCP 的主要特点是数据包以正确的顺序传递,并且所有数据包都被传递。这两个功能非常有用,但对视频流非常有害。

在本地网络上,带宽非常大,而且丢包率非常低,因此 TCP 工作正常。在互联网上,带宽是有限的,每次达到限制都会导致数据包丢失。 TCP 将通过重新发送数据包来处理数据包丢失。每次重新发送都会导致流的延长延迟。

为了更好地理解,试着想象一个数据包包含整个帧(JPEG 图像)。假设链路的正常延迟为 100 毫秒(帧传输的时间)。对于 25 FPS,您需要每 40 毫秒传送一帧。如果帧在传输过程中丢失,TCP 将确保重新发送副本。 TCP 可以检测到这种情况并在 2 倍延迟 - 2 * 100 毫秒内修复理想情况(实际上它会更多,但为了简单起见,我保留了这个)。因此,在图像丢失期间,接收器队列中等待 5 帧并等待单个丢失帧。在此示例中,丢失一帧会导致 5 帧延迟。并且因为 TCP 创建了数据包队列。延迟永远不会减少,只会增长。在带宽足够的理想情况下,延迟仍然相同。

如何修复它

我在 nodejs 中所做的是修复源端。 TCP中的数据包只有在源会做的情况下才能被跳过,TCP自己没有办法。

为此,我使用了事件 drain .
该算法背后的想法是ffmpeg以自己的速度生成帧。 node.js 读取帧并始终保留最后收到的帧。它还具有单帧大小的传出缓冲区。因此,如果由于网络条件导致单帧的发送延迟,则来自 ffmpeg 的传入图像将被静默丢弃(这补偿了低带宽),除了最后接收的图像。因此,当 TCP 缓冲区(通过 drain 事件)发出正确发送某些帧的信号时,nodejs 将获取最后接收到的图像并将其写入流中。

该算法自我调节。如果发送带宽足够(发送速度比 ffmpeg 生成的帧快),则不会丢弃任何数据包,并且将传送 25fps。如果带宽平均只能传输一半帧,则两个帧中的一个将被丢弃,因此接收器将保持 12.5fps 但不会增加延迟。

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

关于node.js - TCP还是UDP?视频流的制作延迟,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59885156/

相关文章:

node.js - Mongoose 文本搜索不返回结果

c - 如何从 Golang 访问 C 指针数组

ffmpeg - 无法编译 libvorbis(缺少 Ogg)

c# - 如何按 block 读取文件

java - Stream使用过程中出现异常如何捕获和修改数据

javascript - Node.js 中的函数构造函数是什么?

javascript - 从 promise 中的回调中检索数据?

image - rawvideo 和 rgb32 值传递给 FFmpeg

Node.js 可读文件流未获取数据

javascript - 如何根据对象数组查询docpad中的文档