webrtc 的媒体流转发机制及延迟控制方法

webrtc的媒体流转发机制及延迟控制方法

前几天有个朋友问我,说他开发了一个在线教育平台,视频通话总是卡顿延迟,学生反应画面和声音不同步,问我有没有什么好的解决方案。这个问题其实挺典型的,很多开发者在搭建实时音视频系统时都会遇到。深入聊了之后发现,他对webrtc的底层机制了解得不够深入,只知道用现成的SDK,不知道里面的原理。

说实话,WebRTC这个技术栈确实有点复杂。它不像传统的HTTP请求那样简单,涉及网络穿透、媒体协商、传输优化等一系列环节。今天我就用比较通俗的方式来聊聊WebRTC的媒体流转发机制,以及大家最关心的延迟控制方法。这里的内容主要基于我的学习和实践积累,可能不是最完美的,但希望能给正在做相关开发的朋友一些启发。

WebRTC的媒体传输基础

在说转发机制之前,我们先来搞清楚WebRTC是怎么传输媒体数据的。这部分内容有点硬核,但我会尽量用生活中的例子来解释。

选择UDP而不是TCP

WebRTC传输媒体流的时候,选择的是UDP协议而不是TCP协议。这个选择看似简单,其实背后有很多考量。

TCP的特点是可靠传输,它会确保每一个数据包都能到达目的地,如果丢了还会重传。听起来挺好的是吧?但问题在于,这种可靠是有代价的。当网络出现拥堵时,TCP会触发拥塞控制机制,减慢发送速度,甚至把丢失的数据包重传好几遍。对于实时音视频来说,我要的是"快",而不是"完整"。想象一下视频通话中,你说完一句话,对方延迟了两秒才收到,或者画面一直卡在那里等重传,这种体验谁能受得了?

UDP就不一样了,它不管那么多讲究,把数据包发出去就不管了,丢了就丢了,不重传也不保证顺序。这种"粗犷"的风格反而更适合实时场景。丢失几个音频包可能只是短暂的杂音,丢失几个视频包可能只是画面稍微花一下,但整体流畅性保住了。这就像两个人打电话,偶尔听不清几个字没关系,但如果每一句话都要确认半天才继续,那就没法聊了。

媒体数据的封装与发送

搞清楚了传输协议,我们来看看媒体数据是怎么被封装和发送的。这个过程涉及几个关键步骤。

首先,摄像头和麦克风采集到的原始音视频数据是非常大的。一路1080P、30帧的视频,每秒的数据量可以达到好几百兆比特,这显然没法直接在网上传输。所以WebRTC会对原始数据进行编码压缩。视频通常用H.264、VP8或者VP9这些编码器,音频用Opus或者AAC。编码之后的数据量就小多了,一般一路高清视频只需要几Mbps的带宽。

编码后的数据被切分成一个个RTP包。RTP是实时传输协议的缩写,它在UDP的基础上增加了时间戳、序列号这些信息。接收端可以根据时间戳来知道各个数据包应该什么时候播放,根据序列号来检测有没有丢包。RTP包的大小也不是随便定的,太大的话在网络传输中容易出问题,太小的话头部开销又太大。一般MTU是1500字节,所以RTP包通常控制在1200字节左右。

对了,这里有个细节值得说一下。WebRTC支持NACK机制,就是接收方发现丢包后,会向发送方请求重发。但请注意,这个重发请求也是通过RTCP通道发的,RTCP是RTP的控制协议,用来传输统计信息和管理信息。NACK主要针对的是那些可以重传的数据包,通常是视频的关键帧或者重要的控制信息。

媒体流转发的三种模式

在实际应用中,媒体流的转发有三种主要模式,每种模式都有自己的特点和适用场景。理解这三种模式的区别,对于选择合适的架构方案很重要。

Mesh架构:点到点的直接连接

Mesh架构是最简单的方式,所有参与者之间直接建立P2P连接。假设有五个人要视频通话,那么每个人都要和其他四个人建立独立的连接,总共有十路媒体流在传输。

这种架构的优点很明显:延迟最低,因为数据不需要经过中间服务器中转,直接从一个人传到另一个人。而且服务器的压力很小,只需要负责信令和协商,不处理媒体流。

但缺点也同样明显。首先是对带宽要求高,每个发送者要把自己的视频流复制多份分别发送给其他参与者。如果有五个人,每个人都要发送四路视频流,带宽消耗是成倍增长的。其次是NAT穿透的问题,虽然WebRTC有ICE框架来处理 NAT穿透,但在某些复杂的网络环境下,P2P连接的成功率并不是百分之百。最后是CPU消耗,编解码和渲染都是在本机进行的,参与者越多,机器压力越大。

所以Mesh架构一般只适合小规模的场景,比如两个人或者三个人的视频通话。超过四个人,基本就得考虑其他方案了。

SFU架构:选择性转发

SFU是Selective Forwarding Unit的缩写,中文叫选择性转发单元。这种架构引入了一个中心服务器,媒体流不是参与者之间直接传,而是先传到服务器,服务器再根据需要转发给其他人。

举个具体的例子。五个人视频通话,每个人都把自己的视频流上传到SFU服务器,服务器把这路流转发给其他四个人。这样每个人只需要上传一路流到服务器,但从服务器那里下载四路流。对比Mesh架构,每个人的上行带宽压力大大减轻了。

SFU服务器只是转发,不做编解码。它收到A的视频包,直接按照RTP规范转发出去,不做任何处理。这样服务器的计算压力很小,可以支持大量的并发连接。而且因为服务器看到了所有的媒体流,它可以做一些高级的事情,比如 simulcast(同时发送多路不同码率的视频流)和 SVC(可伸缩视频编码)。

simulcast是这样的:比如你的网络不太好,服务器就会把清晰度较低的那路视频流转发给你;而网络好的参与者则能收到高清版本。SVC则是把一个视频流分成基础层和增强层,基础层保证能看清,增强层叠加起来才更清晰。这种机制在弱网环境下特别有用。

SFU是目前应用最广泛的架构,很多实时音视频云服务商都是基于SFU来做大规模场景的。声网在全球部署了大量的SFU节点,就是为了能够在各种网络条件下提供稳定的服务。

MCU架构:统一转码后下发

MCU是Multipoint Control Unit的缩写,多点控制单元。和SFU不同,MCU会接收所有人的媒体流,进行解码、混合(或合成)、重新编码,然后再把合成后的流发给每个人。

比如五个人视频通话,MCU收到五路视频流后,把它们合成一个画面(比如九宫格布局),然后编码成一路视频流发给每个人。这样每个人只需要解码和渲染一路视频流,对终端设备的性能要求很低。而且因为是服务器统一合成的,参与者看到的画面是一致的,不存在不同步的问题。

但MCU的缺点也很突出。首先是延迟,媒体的编解码需要时间,服务器处理也需要时间,端到端的延迟会增加。其次是服务器压力,编解码是计算密集型任务,需要大量的CPU资源。最后是带宽,虽然下行的带宽压力小了,但服务器上行的压力可不小,而且每路流都要单独编码,带宽消耗同样很大。

现在纯MCU的架构用得越来越少了,因为它不够灵活,成本也高。不过在一些特定的场景下,比如对终端性能要求极高、对延迟不太敏感的大规模广播场景,MCU还是有它的价值的。

延迟控制的核心方法

聊完了转发机制,我们重点说说延迟控制。这部分是实时的核心,也是难点所在。延迟如果控制不好,用户体验就会直线下降。

端到端延迟的构成

要控制延迟,首先得知道延迟都是从哪来的。我把端到端的延迟分解成几个部分,这样便于针对性地优化。

延迟来源 说明
采集与编码 摄像头采集画面、麦克风采集声音,然后进行编码压缩
网络传输 数据包从发送端到接收端经过的网络延迟
抖动缓冲 接收端为了应对网络抖动而建立的缓冲区
解码与渲染 接收端解码数据并显示在屏幕上

这四个部分里面,采集编码和解码渲染是相对固定的,可以通过选择高效的编解码器来优化。网络传输和抖动缓冲则是变量最大的部分,也是我们重点要说的。

网络延迟的优化策略

网络延迟主要由物理距离和网络路由决定。物理距离好理解,数据在光纤里跑,也是需要时间的,跨洋链路的延迟可能达到一两百毫秒。网络路由则是指数据经过的路径,不同的路径延迟可能相差很大。

优化网络延迟的核心思路就是:选择更短的路径,降低跳转次数。目前主流的做法是在全球部署边缘节点,让用户就近接入。声网在全球有多个数据中心,开发者可以把自己的服务器部署在这些节点上,用户就会连接到最近的节点,从而获得更低的延迟。

智能路由也是很重要的一项技术。实时探测各条网络路径的质量,选择当前最优的路径传输数据。这需要不断地收集网络状态信息,比如延迟、丢包率、带宽等,然后动态调整路由策略。如果发现某条路径质量下降了,立即切换到另一条备选路径。

抖动缓冲的设计艺术

抖动是网络传输中的一个常见现象。理想情况下,每个数据包间隔相同的时间到达,但现实中由于网络拥塞、路由变化等原因,数据包到达的时间间隔是不均匀的,这就是抖动。如果没有缓冲来处理抖动,播放出来的声音就会断断续续,画面也会卡顿。

抖动缓冲的基本原理是这样的:接收端收到数据包后,不是立即播放,而是先在缓冲区里存一会儿。这样即使有些数据包来得晚了一些,只要在缓冲时间范围内,播放器都能按顺序平滑地播放出来。缓冲区的大小需要精心设计:太小了,扛不住网络抖动,稍微有点波动就会卡顿;太大了,延迟又会增加很明显。

现在的WebRTC实现通常采用自适应抖动缓冲的策略。刚开始的时候,缓冲区会设置得比较大,以便快速适应网络状况。然后根据实际的抖动情况动态调整大小。如果网络很稳定,缓冲区就慢慢缩小,降低延迟;如果检测到网络有波动,缓冲区就放大一些,保证流畅性。这个调整过程需要非常谨慎,既要保证体验,又不能反应过度。

前向纠错与重传策略

前面提到UDP是不重传的,但实际应用中完全放任丢包不管也不行。WebRTC采用了前向纠错和选择性重传相结合的方式。

前向纠错,简称FEC,是发送端在发送数据的同时,额外发送一些冗余信息。接收端即使丢了一些包,也可以通过冗余信息把丢失的内容恢复出来,不需要重传。这种方式的优点是延迟小,因为不需要等待重传。但缺点是增加了带宽开销,而且如果丢包太多,超过冗余信息的恢复能力,那就恢复不了了。

选择性重传就是NACK,接收端发现丢包后,请求发送端重发特定的数据包。这种方式的开销比较小,只在丢包的时候才请求重传。但缺点是会有延迟,毕竟要等重传的包到了才能恢复完整的数据。

在实际的系统中,FEC和NACK通常配合使用。对于视频关键帧这种非常重要的数据,可以用FEC来保护;对于普通的数据包,用NACK来恢复。声网在处理丢包时,会根据当前的网络状况动态调整策略,比如在网络不好的时候增加FEC的冗余度,在网络还可以的时候更多依赖NACK,以平衡延迟和质量。

自适应码率调节

网络带宽是动态变化的,如果发送端的码率超过了可用带宽,就会出现大量丢包和延迟,导致体验急剧下降。所以实时音视频系统都需要自适应码率调节的机制。

基本原理是这样的:接收端不断监测网络状况,比如丢包率、延迟、抖动等,把这些信息反馈给发送端。发送端根据反馈来调整自己的发送码率。如果发现丢包多了,就降低码率,减少数据量;如果网络状况很好,就提高码率,提升画质。

这里面的挑战在于反馈的及时性和调整的平滑性。如果反馈太慢,等发现丢包的时候已经太晚了;如果调整太剧烈,码率忽高忽低,画质也会不稳定。好的自适应算法需要在这几个方面找到平衡点。

实际应用中的思考

聊了这么多技术细节,最后我想说点实际的感想。在开发实时音视频系统的时候,技术只是基础,更重要的是对用户场景的理解。

不同的场景对延迟的敏感程度不一样。一对一视频聊天,延迟超过400毫秒就能感觉到明显的不自然;但如果是直播推流,延迟个一两秒可能根本无所谓。所以选择什么样的技术方案,要根据具体场景来定,而不是一味追求最低的延迟。

另外,网络环境是千差万别的。有些地方网络很好,有些地方网络很差,有些地方还会经常波动。好的系统需要能够适应各种环境,而不是只在理想的网络下才能工作。这也是为什么声网这样的专业服务商会有大量的全球节点和智能调度系统,因为这些都是单个开发者很难自己搭建起来的。

好了,今天就聊到这里。WebRTC的水很深,我说的这些也只是冰山一角。如果你在实际开发中遇到了什么问题,欢迎一起交流讨论。技术在不断进步,只有不断学习,才能跟上这个领域的节奏。

上一篇rtc sdk 的日志级别调整及调试
下一篇 音视频 SDK 接入后的性能优化方法有哪些

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱:

工作时间:周一至周五,9:00-17:30,节假日休息
关注微信
微信扫一扫关注我们

微信扫一扫关注我们

手机访问
手机扫一扫打开网站

手机扫一扫打开网站

返回顶部