[转载] RTC 弱网对抗之冗余策略
背景
当下社会,实时音视频通话已经成为人们生活、工作中重要的组成部分,如商务会谈、亲朋聊天等。而在通话过程中,总会存在着这样那样的意外情况:可能你坐在飞驰的高铁上——信号时好时坏;又或者在会议途中离开办公室——网络从 wifi 切换到 4G……实现高质量的实时音视频通话需要搭建一座无视距离连接人们的“桥梁”,而这座“桥梁”需要优秀的“基建技术”来保障网络传输的稳定性和可靠性。
网络传输链路上存在着许多不稳定的情况,造成所发出去的数据包出现丢包、延时或者抖动。不稳定的原因有很多,两个通讯双方在物理空间上存在距离,传输过程经过很多设备的处理,中途存在线路硬件故障、软件驱动限制、或者链路数据拥塞等情况,都会导致发送出去的数据包在接收端没有办法收到或者延迟收到。
我们可以通过一些简单基础的工具检测网络是否处于波动情况,比如 ping 命令或者 Iperf 工具可以帮助统计丢包、延时以及单向抖动。我们用 ping 命令向一个远端的主机发送 ICMP 消息,远端主机会对你进行应答,这个过程中如果你发送出去的 ICMP 消息丢了,或者远端服务器应答的消息丢了,在命令行界面上,对应的 icmp_seq 就会显示超时未收到,这种情况就直观地体现了丢包。
ping vs iPerf
带宽分配和冗余策略是弱网对抗核心模块,随着算法的演进迭代,我们能够保证线上大部分场景的优质音视频体验。对于一些小概率或特殊场景,如突发网损、拥塞恢复、PPT 翻页等,我们还引入了多种冗余策略,来兼顾流畅、恢复效率和低延时的需求。
常用包恢复技术
通常,一个数据包在网络传输链路中丢失了,主要有两种方式将包进行恢复:一种是发送端利用接收端通知或者超时的机制,重新将这个包发送过来;另一种则是基于其他收到的冗余包,在接收端将该包恢复出来。
第一种是我们熟知的自动重传请求 ARQ 技术。与 TCP 协议中的 ACK 应答机制不同,实时音视频场景使用的是 NACK 否定应答机制,通过接收端检查包序号的连续性,主动将丢失的包信息通知给发送端进行重新发送。这种方式的优点是在低延时场景下的恢复效率高,带宽利用率好,但在高延时场景下的效果比较差,存在重传风暴等情况。
第二种是 Forward Error Correction (FEC) 即前向纠错编码,一种通过冗余发送对抗网络丢包的技术。它主要的技术原理就是分组编码,组内进行冗余恢复。假设每个分组由 k 个媒体包和 r 个冗余包组成,一个分组中 k+r 个数据包中任意 k 个包可以用来重建 k 个原始媒体包。这种方式的优势是根据先验知识进行冗余决策,不受延时影响。
冗余策略
在上述基本的包恢复技术下,为了使各种场景的整体抗弱网能力最大化,需要针对带宽分配、抗丢包技术的组合配置等进行一系列的优化,从而达到抗丢包能力、端到端延时、卡顿率、冗余率的平衡,达到“消耗最小的代价,实现最优的体验”。
下面具体介绍我们在这方面做的几项优化。
自适应调整策略
冗余策略大致可以分为两类,一类是前向冗余,一类是被动冗余。按照前文的描述,我们知道前向冗余的优点是不需要交互,在高延时环境下更加适用,缺点是带宽占用过多。被动冗余的优点是按需发送,占用带宽较少,缺点是高延时场景效果会急剧下滑。
我们的冗余策略则是在寻找一个平衡点,通过被动冗余和前向冗余策略比例的调整,在保证丢包恢复率(比如 99.5%)的前提下,尽量的减小冗余占比,尽量的减小抗丢包恢复时间。所以,我们可以将当前问题抽象成如下一个数学场景:
假设 m 个媒体包,在重传 k 次后,收到 i 个包的概率记为: P(m, i, k)
假设 n 个冗余包,接收端收到 j 个包的概率记为: P(n, j, 0)
根据当前的 FEC 算法,这个 m + n 的分组在重传 k 的情况下,接收端只需要接收到任意 m 个包,就能够恢复全部的媒体包, 这个概率为:
Nack FEC 算法就是基于恢复概率大于 99% 的情况来计算当前最少需要配置的 FEC 冗余度 n
基于上面的模型,在 m (平均帧分组大小), k (允许时间范围内的重传次数), i,j 是可以自变量。我们只要将 n 从 0 开始代入,上面求和的结果大于 99%,就可以将这个 n 作为 FEC 的冗余率。
通过上面的算法,我们定义一个抗丢包恢复时间 resend_delay,就可以获得被动重传次数 k。通过参数 k,可以推导出为了达到恢复率 99%,还需要的 FEC 比例。
上述就是理论上的最优冗余率计算逻辑。通过该算法逻辑,我们可以获得一个自适应的冗余调整策略,从而获得最优的冗余比例、最合适的延时损耗、以及最佳的丢包恢复率。
可靠重传策略
在接收端媒体缓存中,对于 seq 最新和最老范围内没有接收到的数据,接收端会发送 Nack 请求,然后发送端接收 Nack 请求,将相应的包传送过来。
正常工作状态中,Pacer 优先发送 RTX 重传数据,然后再发送媒体数据,这样接收端这边较老的丢失数据包,总能够优先得到恢复。
但是在一些场景下,比如大丢包(70% 或以上的丢包率),或者突然限宽(4M —> 300K)时,会出现大量的数据包被丢弃掉。这个数据包既包括原始包,也包括 Nack 重传包,这样会导致如下图的一些问题。
如图,如果丢包恢复效果比较差,比如上面 n + 3 的帧已经开始发送了,但是 n 帧还没有被接收端全部接收到,这时,接收端生成的需要重传的包列表 nack_list 就会很长。对于发送端来说,由于媒体数据还在继续发送,理论上接收端请求的 nack 会越来愈多,这样就会形成 nack 风暴。
Nack 风暴不但会导致大量的重传流量挤占媒体带宽,导致带宽分配模块分配给媒体的码率降低,也会导致 Pacer 拥堵,从而带来更大的端到端延时。同时,由于不是选择性的重传某个帧的媒体包,导致接收端需要解码的帧能够被完整丢包恢复的概率比较低。
为了避免上述情况,我们引入了可靠重传模块:在检测到上述情况后,及时暂停媒体发送,同时全力保障已经发送的帧数据能够完全恢复。
我们通过 TccAck 和 Nack 请求的信息来确定某一个包处于什么状态,然后统计当前已经发送的数据中没有被完整 ACK 的帧数量。如果这个数量过大,则会暂停媒体发送(同时暂停编码器)。暂停媒体发送后,按照帧从老到新的顺序,把 pacer 预算分配给最高优先级的帧,主动发送这些帧里面没有被 ACK 的数据。
通过上面的方式,我们有效解决了目标场景长时间卡死或者卡顿过多的问题。
拥塞恢复场景下快速抑制 FEC 码率
我们 FEC 使用 loss 计算 FEC 冗余率,为了防止抖动带来的剧烈变化,这个 loss 值被平滑过。但是对于一些场景,比如带宽突然掉落到较低的场景下,当拥塞状态解除后,网络丢包就会消失。这个时候,我们需要快速的抑制 FEC 码率,让出带宽给到媒体,这样可以尽快地提升画面质量。
使用上面的状态机,在接收到拥塞解除信号(拥塞状态解除,并且瞬时 tcc loss 变为0)后,在平滑 loss 没有变为 0 的情况下,使用瞬时 loss,可以快速取消掉 FEC 冗余。
空余带宽利用优化
在共享场景下,冗余策略会遇到进一步的挑战。比如在 PPT 不翻页的时候,空余带宽需要让给视频。但是在翻页的时候共享流又会迅速占据带宽。这样就会导致视频码率在翻页时出现较大的波动。如果整体可用带宽较低,就能够看到视频质量的明显变化——卡顿率、延时上升。
如图,在 PPT 翻页场景,video 会使用 screen 空余未被使用的带宽。静止画面下,video 使用了大部分原本共享的预算。一旦 PPT 翻页,共享的码率瞬时提高,而 video 没有及时把编码码率降下来,就会造成 pacer 模块的拥堵,导致丢帧从而摄像头流卡顿。
针对该场景,我们对带宽分配策略进行了优化:
缓升快降
- 带宽分配过程中对每个媒体流使用的上一层传过来的空余码率进行缓升快降操作,防止高优先级码流过快让出空余带宽,导致低优先级码流的分配码率大幅波动。
关键帧检测
- 当某个媒体流开始收到关键帧的时候,降低该流让出空余带宽的量,使得整体输出码率的波动性降低。
波峰检测
- 使用 300ms 统计窗口判断波峰,出现较大的波峰数据时,降低该流让出带宽的量,减小整体输出码率的波动性。
优化效果
在上述的冗余策略优化下,飞书会议整体冗余率大幅度降低(部分上涨是由于原有算法冗余不足,出现大量卡顿,算法优化后,整体冗余率调整为最佳值)。同时,在保证了音视频整体效果的前提下,整体端到端延时也进一步下降,提升了飞书会议的整体音视频体验。
算法优化大幅度降低了低延时场景的冗余率,高延时场景的冗余率也趋于理论上的合理值。
解决了原有算法在高丢包场景下的问题,对于高延时,高丢包率的弱网场景下的体验大幅提升。
解决了场景,网络状态变化过程中的收敛速度,提升了变化过程中的音视频体验。
在与同类产品的比较中,我们优化算法的冗余度明显低于同类产品,在流畅度对齐的情况下,消耗更小的带宽。特别是在高丢包,高延时场景下,我们的冗余率只相当于同类产品的 40%。
未来展望
通过这些经优化的冗余策略,当前飞书会议在弱网场景下的卡顿率、端到端延时、冗余率等指标得到了大幅度的优化。在此基础上,我们未来会在极端弱网支持,带内冗余联动策略,智能场景识别等方面,进一步加大投入,增加飞书会议在各种弱网场景下的客户端体验。
极端场景下的抗弱网能力支持
增加针对极高丢包、极高延时、丢包+延时,低带宽+高丢包等场景的支持,配合冗余策略以及带宽分配策略,达到极端弱网场景下面的最佳音视频体验。
带内冗余联动下的抗弱网策略
冗余策略,除了前面提到的非音视频编码内的带外冗余(Nack,FEC 等),也包含音视频编码内的带内冗余。相比于带外冗余,带内冗余往往结合编码自身的特点,使用更少的冗余码率代价,达到更高的抗弱网效果。
我们的冗余策略也会进一步结合、更大化利用带内冗余算法,进一步提高抗弱网能力,降低整体延时和带宽占用。
场景识别下的抗弱网策略优化
后续,我们还将对当前的网络环境进行识别,获取到当前的网络场景,从而可以进一步对冗余策略以及编码策略进行预判式的策略下发。
比如,我们识别到当前场景为高铁场景,就可以在冗余策略生成的时候偏向于抗连续丢包的策略,也可以预先增加默认前向冗余来增加突发弱网的抗性。根据识别场景的特性,可以在实际发生弱网之前以及发生弱网之后,做出策略上的区别。