WebRTC Qos优化杂记

1 背景

WebRTC的开源极大降低了实时音视频互动这个领域的开发门槛,使用WebRTC的公司和开发者越来越多。WebRTC作为一个开源项目,大而全,虽然配套了比较全面的Qos优化方案,但是并不是针对具体的产品,也不对最终Qos负责,默认实现的的抗丢包、延迟、抖动效果并不太尽如人意,只能说拥有很大的潜力。

开发基于WebRTC的系统可能很简单,但是做一个基于WebRTC的好产品却很难,因为Qos的优化是一个门槛,这个门槛主要体现在两个方面:

  • 接入端,带宽估计、NACK、FEC、PLC等弱网抵抗算法的优化;
  • 服务端,节点的覆盖率(钱),调度系统、路由分发算法的优化。

本文是笔者对WebRTC Qos性能研究、优化的一些思考,有一些已经经过验证,有一些可能不完全准确,希望对有兴趣的读者有一些启发。

2 WebRTC主要的抗弱网算法

弱网主要包括:

  • 丢包;
  • 大延迟;
  • 抖动;
  • 带宽受限。

WebRTC的抗弱网算法主要包括:

  • GCC:网络丢包、抖动、延迟、过载探测,带宽估算、带宽分配;
  • NACK:丢包重传,属于冗余包,用于发送端低延迟丢包场景;
  • FEC:前向纠错,属于冗余包,用于发送端高延迟丢包场景;
  • RED:冗余包,用于发送端在丢包场景下发送音频冗余包;
  • PLC:丢包隐藏,用于音频接收端的丢包处理;
  • JitterBuffer:抖动缓存,用于接收端抖动、丢包处理,延迟换流畅。

3 保守的拥塞控制

WebRTC拥塞控制算法GCC的总体流程是:预估当前网络的可用带宽,并将可用带宽分配给编码器的目标码率和冗余包带宽(重传包 + FEC)。因此预估带宽的准确性直接决定了冗余包的量,也就是能够抵抗弱网的程度。

WebRTC使用GCC来做拥塞控制,可以参考我的文章《WebRTC GCC拥塞控制算法详解》,总体思想是发送端根据丢包率调整预估带宽,接收端根据网络延迟的变化(抖动)来调整带宽,综合发送端、接收端两端的预估带宽决定最终预估带宽。简单说就是:发生丢包,降低预估带宽,发生抖动,降低预估带宽。实测可以发现GCC对抖动非常敏感,很短一段时间的延迟变化将迅速降低接收端预估带宽,从而降低目标码率、以及冗余包的量,提高卡顿率。而对手机端来说,长时间的连麦发热会造成网络性能下降,抖动可能变成常态。

WebRTC在最近的版本中对GCC做了一些优化,核心的改变是将接收端的带宽预测放到发送端,将网络抖动的计算从卡尔曼滤波器换成了Trendline filter,做了一些抗抖动的优化。但是接收端的带宽预测可以让服务端更简单直接地控制预估带宽,所以各有利弊。

如标题所言,个人感觉这样的拥塞控制策略过于保守,过于退让。的确是会有带宽受限的情况,但是:

  • 中国的绝大多数网络环境上行带宽并不会低于512K;
  • 如果是同一网络内其他应用争抢带宽导致的丢包、抖动,作为一个需要保证Qos质量的应用,为什么要让而不抢?
  • 如果只是网络噪声,设备发热等问题,降低整体的预估码率,缓解卡顿的作用有限,质量(清晰度、帧率)下降却很明显;

如果能够在确认的确是带宽受限之前,做好正确的处理,那么就可以保证给冗余包足够的带宽,从而在大部分弱网的情况下保证质量,因此实际上可以尝试:

  • 综合机器学习的结果来预估用户的初始带宽;
  • 发生丢包、抖动,并不马上降低整体的预估带宽,而是先降低编码器目标码率,给冗余数据分配足够带宽,多发多抢;
  • 如果仍然无法保证有效数据的丢包率,再降低预估带宽。

4 保守的NACK

NACK模块中一个包的重传次数根据网络延迟有一定的限制,例如网络延迟25ms,请求1个包重传需要1个RTT 50ms,那么1秒内理论上来说最大可以重传1000 / 50 = 20次。但是WebRTC的NACK模块中RTT却是默认的100ms无法更新,即使在更低的RTT下也是如此,也就是说1个包最大只能重传10次,在低延迟高丢包的场景下造成卡顿。

这个时候可以跟通过SR/RR包计算RTT一样的算法,通过接收端发XR包,发送端响应,在接收端计算到实际的RTT并设置到NACK模块,同时设置一定的阈值,保证RTT的合法性。这样就能够处理低延迟高丢包的场景,通过这个优化可以在低延迟的场景下达到50%以上的抗丢包。

5 保守的FEC

在高延迟场景下,NACK的贡献比较有限,因为RTT过高导致1秒内重传的次数有限,稍微连续丢包就需要很久的时间重传,这个时候FEC的作用就体现出来了。

音频在NACK、RED、Inband Fec、PLC等机制的加持之下,可以做到70%的抗丢包。这里主要说的是视频(H264)的FEC,目前有两种:

  • UlpFec:复用了音视频流,与NACK不能同时打开;
  • FlexFec:独立的SSRC,试验性质,默认不打开,实际上在高延迟场景下非常有效。

鉴于目前大多数场景媒体流都需要服务端通过SFU、MCU来转发,对音频来说,Inband Fec属于带内的FEC,只需要推流端协商useinbandfec这个字段即可打开,在丢包的情况下会触发这个功能,拉流端自动检测并解码FEC包。对视频来说,FlexFec则属于带外的FEC,需要将FlexFec的解码模块移植到服务端,并修改SDP使能FlexFec,服务端解码FEC包恢复原包后再转发。

6 弱网级别定义

目前WebRTC的弱网几个主要指标丢包、延迟、抖动中,对延迟、抖动的计算是相对准确的,但是丢包率的计算有点问题。

目前这个丢包率是在通过NACK、FEC等算法恢复后还是无法处理的丢包,并不能体现网络实际的丢包情况。也就是一旦出现这个丢包,那就意味着很大可能已经要卡顿了。目前的GCC算法发送端在弱网下的调整思路就是通过降低预估带宽期望丢包率降到接近0,然后觉得网络可以了又开始提高预估带宽,实际上网络是不行的然后可能又丢包了,然后又去调低预估带宽,接收端基于延迟的算法也类似,如此循环往复,不停的尝试,期望达到一种动态的平衡,你在弱网下看到周期性的卡顿可能就是这个原因。

实际上,可以考虑根据丢包、抖动、延迟等指标,对弱网情况划分几个等级,在每个等级中,指定总的预估码率、目标码率、冗余码率,输出帧率、分辨率等。

弱网等级丢包抖动延迟
1lost1jitter1delay1
2lost2jitter2delay2
3lost3jitter3delay3
4lost4jitter4delay4
弱网等级总码率目标码率冗余码率输出帧率输出分辨率
1bw1target_bw1red_bw1fps1res1
2bw1traget_bw2red_bw2fps2res1
3bw1target_bw3red_bw3fps3res1
4bw2target_bw4red_bw4fp4res2

总码率 = 目标码率 + 冗余码率

如上表,遵循一些原则:

  • 总码率相对固定,只有在无论如何调整都无法恢复的情形下降低
  • 主要根据弱网等级调节目标码率和冗余码率,网络好,则目标码率比重较大,网络差,则冗余码率比重较大;
  • 在低码率情况下,优先调节帧率,最后才调节分辨率,流畅换质量;
  • 不去试探网络的底限,预先设定各个等级流畅的边界。

在这些前提下,需要得到网络的真实丢包率,这个可以通过SR包来计算,SR包中携带了发送的包数,可以在接收端得到,并且统计实际收到的包数,计算出真实丢包率后通过XR包等方式反馈给发送端。

7 JitterBuffer延迟换流畅

音频的JitterBuffer比较简单,就是一个排序缓存,这里说的是视频的JitterBuffer,参考我的文章《WebRTC视频JitterBuffer详解》,以及《WebRTC音视频同步详解》

在丢包+延迟+抖动的弱网环境下,推流端通过NACK+FEC等方式做了一定的容错,但是可能并不能百分之百解决所有问题,而且通过SFU分发到拉流端之后,拉流端网络也可能有问题,这个时候需要JitterBuffer来做媒体包的缓存,例如视频JitterBuffer,它不只缓存视频包,还组帧,还期望组成连续的可解码的GOP,一旦有一个帧的包丢失,就需要等待,如果较长时间无法恢复就丢弃剩余的整个GOP,如果恢复的比较晚,等到音视频同步完去渲染发现这一系列帧已经太晚了,会直接扔掉或者加速播放,这些都会造成播放端的卡顿、不流畅的体验,这是WebRTC JitterBuffer比较注重实时性的体现。

但是这个时候你可能发现接收端收的音频、视频数据量如果缓存足够长时间、并且能够平滑的播放的话,实际上并不会造成卡顿,只是需要付出增加缓存时间的代价,也就是所谓的延迟换流畅

可以考虑记录一段时间内接收到的帧间间隔的最大值,把这个值加到JitterBuffer的目标延迟上,人为的增加缓存时间,这个时候音视频同步机制会进行追赶,一段时间后会自动同步,当然,网络状态恢复的时候需要可以恢复到低延迟的原状。

8 弱网状态的传播

如果推流端的弱网情况并没有被NACK+FEC完全恢复,媒体数据通过SFU后,RTP包的序列号空洞被带到拉流端,拉流端也会触发NACK,要求拉流端的SFU重传丢失的包,但是可能SFU根本没有这个数据,进而导致大量的无用重传请求。推流端恢复的越差,这种情况越恶劣。

可以考虑在服务端集成JitterBuffer,组成完整帧后再转发序列号重新连续编号的RTP包,这样可以减少这种现象的发生。

9 统计指标以及监控

可以参考声网的水晶球系统。一次通话质量的可监控、可追溯是产品化的必要条件。事实上可以比水晶球做的更详细,具体的就是把chrome://webrtc-internals中码率、帧率、丢包率、延迟、JitterBuffer长度等关键Stats数据发到服务端。

同时添加自定义的一些数据,例如用户信息、流信息、接入点、地域、 运营商等,由日志系统进行处理并展现。

这些数据以及对应的图表是Qos优化、问题排查的最主要来源。

10 路由和分发

参考我的文章《基于WebRTC的直播CDN》

11 总结

综上,WebRTC弱网Qos优化的一些原则:

  • 综合各种手段得出相对准确的实际带宽;
  • 给冗余数据(NACK/FEC)足够的带宽,多发,但是要在接收端统计并调节其无效冗余数据的比例;
  • 延迟换流畅,流畅是第一优先级;
  • 你必须有一个监控系统来直观反馈你的调优结果。
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页