背景
实时音视频的最显著的特点是低延迟,也就是说实时音视频对于网络的要求是非常高的,甚至可以说是矛盾的。一方面它为了追求低延迟,它能够允许网络传输中的丢包,另一方面因为视频编解码的传递参考性,任何的丢包都会造成很大的视频质量的损失,最终将降低实时通话的QoE评价,所以又不能容忍网络传输中的丢包。为此搭建一个高质量实时音视频系统,尤其是多方参与的会议系统,应对于参与通话各方上下行网络复杂性(带宽受限/丢包/抖动/高时延)的传输QoS策略的设计是非常有必要的。本文就会议场景下,服务器端的QoS策略,做一些详细的分享。
会议场景分段QoS
上行QoS
丢包重传请求
我们设计了灵活NACK请求协议,其协议设计可以很好的解决上述情况:
协议允许单个NACK请求包对多个流,进行不同丢包情况的重传请求。
前向纠错
前向纠错(FEC)其实是一种冗余错误恢复的算法。应用在网络传输中,主要是用来抵抗网络丢包导致的信源错误。其具体的做法就是将原始的信源数据进行可逆的运算,生成额外的冗余包,在实际发送的时候会将原始包和冗余包分为一组发送到网络上。在网络出现丢包的时候,接收端可以通过同一分组的原始包和冗余包进行逆运算去还原所丢失的原始包。当然这里的还原是有条件的,还原的成功率跟我们冗余算法的冗余度大小有关,一般情况下成正相关。但过多的冗余包会占据很多的发送流量,这样其实是不利于正常的媒体传输的。
关于冗余算法的选取在服务器端也是非常重要的一个课题,主要涉及到两点:
一、冗余度在算法层面是否支持动态调整 二、计算复杂度能否满足服务器性能要求。
第一点很容易理解,因为网络丢包是复杂多变的,固定冗余度的算法要么会带来带宽的浪费要么恢复效果不理想。
第二点的话,源自于实时音视频服务器主要的角色是做媒体分发的,其根本要求是分发的高效,一旦FEC的算法复杂度过高,在高并发高I/O吞吐的情况下,势必会降低服务器并发性能,甚至会引入额外的端到端的delay。目前已经实现的FEC算法很多,简单有基于异或的实现,复杂的有基于矩阵运算的,以及一些其他的算法,这里就不详细描述了。
网易云信实践
以上简单的介绍了丢包重传请求和前向纠错的策略。这两种机制是常见的抗丢包策略。在具体的应用上,各个音视频厂商,只是在协议实现和算法选取上略有不同而已。既然ARQ和FEC都是为了对抗丢包而制定出来的策略,那么在网易云信中我们如何做最优的策略选取的呢?
首先在做策略选取之前,我们先回顾一下两种策略对抗丢包的不同思路。
第一种丢包重传策略(ARQ),解决的是当前丢包已经发生,需要做短暂的时延牺牲(即做丢包重传的时候会造成Jitter),来对抗丢包,其优点明显,即不会实时的占用信道带宽,缺点就是引入的Delay,一些不当的NACK请求策略的设计甚至会造成流量尖刺。
第二种FEC策略,其作用效果是实时观测丢包率的变化,做丢包率的预估,实时产生冗余数据来对抗可能出现的丢包。其优点在基本不需要牺牲时延,可以做快速的丢包恢复。但其缺点亦很明显,即依赖于丢包预测的准确度,过高的冗余会造成带宽流量的浪费,甚至挤压正常的信源的传输,导致一个不健康的信道传输状态。
- 建立网络状态的观测器。
2. ARQ手段先行,FEC手段做兜底。
3. 基于接收端反馈以及模块自检的策略调整。
网络本身是复杂的,需要有一个合理的调度器来控制ARQ和FEC抗丢包模块的协同作用,要做到具体网络情况做具体的调节,我们的抗丢包策略不应造成过多的网络信道的挤压,进而反向压制了信源的质量,甚至导致拥塞的时而发生,这样将会和我们的抗丢包策略设计的初衷背道而驰。
下行QoS
- 设计出下行接收端可弹性接收流组合的方案。
- 准确的探测出用户真实的下行带宽。
- 制定合理的带宽分配方案
- 进行流量的平滑发送以及拥塞控制
设计合理的接收端可弹性接收的方案,是整个下行QoS的设计基础。一个完整的拥塞控制系统,其必然要完成两个基本的工作,第一点是要感知当前信道的传输状态,这个状态可以是信道的最大带宽,也可以是当前合适传输速度。第二点是能够根据这个状态对发送信源进行调节,将信源的码率控制在可承受范围之内。所以,对于服务器端下行拥塞控制而言道理也是一样。服务器设计了多流+SVC的机制进行下行流量的控制。来实现对下行传输链路的控制,应对于用户不同的下行状态,做到最佳的QoE效果。
多流机制
带宽探测
选取BBR算法两个最主要的原因:
1、基于可测量的准确带宽 。
2、算法层面的最大带宽利用率。
服务器下行和客户端上行拥塞控制的本质区别在于,下行要求的带宽大。客户端上行可以通过平滑的调整信源做到最佳的发送速率,而服务器只是基于现有上行流的转发,要做到平滑的信源调节非常困难。所以客户端的拥塞控制一般使用GCC比较多,原因在于GCC更侧重于发送码率的动态调节来进行拥塞控制。BBR本身的算法这里就不具体描述了,Webrtc对BBR在是视音频场景下的应用也有代码的实现,处于测试阶段。
- Probe_RTT 阶段的隐藏弱化
- 上行网络丢包带宽补偿
- 上行网络RTT突变以及高Jitter场景优化
- 下行链路抖动以及丢包的优化
- Padding流量的优化
- 快速上探机制的实现
带宽分配策略
带宽分配策略。带宽分配模块要做的事情,其实就是结合下行用户的探测出来的带宽,以及下行用户的原有的订阅关系,帮用户智能的选取最佳接收流的组合。在介绍具体介绍带宽分配策略之前,简单回顾一下我们下行QoS的设计基础,即多流+ SVC的机制:用户上行的多流中的每条流都会在发布的时候把自己的可调控码率空间信息,即档位信息告知发布订阅管理器。通过这样的实现,服务器便可制定灵活的带宽分配方案。
- 源端无感知的,接收端大小流切换
- 源端配合的流内码率(档位)调整
1. 尽可能不去反向压制发送端的编码码率,在基于可选取的大小流的基础上做最佳接收流的组合,来匹配用户下行可实际接收的带宽。
平滑发送及拥塞控制
平滑发送及拥塞控制。前面几个模块讲的是用户怎么接收的问题,最终如何控制下行的发送,交由这个模块来管理。这个模块的设计的每个细节和用户体验息息相关。大的思路有如下几点:
- 平滑发送。就平滑发送而言,我们主要的实现是创建Pacer对象,在单独的发送线程中,起高精定时器,进行发送的平滑,让网络流量不会在观察区间内有Burst的现象。
- 基于流级别的优先级策略以及可选择性发送策略。缓冲在发送队列的流,会有不同的优先级。在我们的默认设计里优先级顺序为 重传包 > 音频包 > 大于视频包 > 被切掉的流 > Padding包。另外也设计了用户态的流的优先级。总体策略是用户态优先级最高,然后再参考QoS本身的优先级。可选择性发送策略的设计主要基于SVC,观察Pacer的堆积情况,以及对应流的堆积情况,进行转发分层的选取。
- 拥塞避免。这个模块的实现,主要是从BBR带宽探测模块获取建议的发送码率,并且严格按照码率发送,在某些场景下如真实媒体数据不足的情况,甚至可以减少发送码率。当然这些行为都能够让带宽探测模块获感知到。
- 拥塞缓解。这里的拥塞缓解更多是模块内部拥塞状态的缓解。主要的方法是通过获取发送队列的堆积情况,来进行模块内部拥塞状态的判断。一旦源端超发或者带宽分配模块调节没那么灵敏,导致模块出现拥塞状态。那么就要及时的进行堆积的处理,而不是把数据放到网络上,造成网络的拥塞,带来更多不确定因素。这里堆积判断,主要基于Trendline 配合绝对Delay的固定阈值。
平滑发送及拥塞控制总图