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同步协议和序列。

我的问题是,TCP真的会造成这样一个问题吗?在非常慢的动作下,延迟会达到10秒?因为在本地网络上,它运行得很好。

是的,TCP绝对不是正确的协议。可以使用,但需要在源端进行一些修改。不幸的是,UDP并不是一颗神奇的子弹,如果没有额外的逻辑,UDP也不会解决问题(如果你不在乎,你会看到从其他帧随机构建的坏帧)。

解释

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

在本地网络上,带宽相当大,而且数据包丢失也很低,因此TCP运行良好。在互联网上,带宽是有限的,每次达到这个限制都会导致数据包丢失。TCP将通过重新发送数据包来处理数据包丢失。每次重新发送都会导致流的延迟延长。

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

如何修复

我在nodejs中所做的是修复源代码端。只有当源将跳过TCP中的数据包时,才能跳过它,TCP本身没有办法做到这一点。

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

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

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

在服务器或客户端上,运行wireshark跟踪,这将告诉您数据包的确切"延迟",或者哪一方没有做正确的事情。听起来服务器没有达到您的期望。请确保流是UDP。

这可能与您的用例无关,但这正是基于HTTP的内容传递系统(如HLS)非常有用的地方。

UDP或TCP,试图在互联网上实时传输数据是很困难的。

HLS会吐出可以通过HTTP下载的文件(这在提供大文件时实际上非常有效,并确保视频的完整性,因为它是在纠错等中内置的),然后由播放器在客户端按顺序重新组装。

它可以很容易地适应不同质量的流(协议的一部分),让客户端决定它消耗数据的速度。

顺便说一句,Netflix等公司使用的Widevine DRM有点类似——基于HTTP的内容交付,尽管Widevine会采用不同质量的编码,将其压缩到一个巨大的文件中,并使用HTTP范围请求在质量流之间切换。

相关内容

最新更新