1. 前言
实时音视频通信发送端需要一个平滑发送模块(Pacer),因为视频的关键帧比非关键帧大很多,一般一个关键帧需要打到多个 RTP 报文中,此时如果直接把所有 RTP 报文发送到网络,很容易造成网络拥塞。
WebRTC Pacer 模块的作用就是让数据在网络上发送更加平滑,防止因为数据量的突增造成网络发生拥塞,效果如下。
假设是只发送音频数据包,平滑发送模块作用较小,因为音频帧产生的时间间隔是固定的,而且一个音频帧编码后的数据不大,一般一个 RTP 报文可以承载。
2.WebRTC Pacer
2.1数据包传入Pacer模块的队列
我们以发送视频包的函数调用栈为例说明数据包是如何被传入 Pacer 模块的。
编码器完成帧图片编码后会回调 RtpVideoSender::OnEncodedImage 函数,然后调用 RTPSenderVideo::SendEncodedImage,再调用 RTPSenderVideo::SendVideo,之后调用 RTPSenderVideo::LogAndSendToNetwork,该函数会调用到 PacedSender::EnqueuePackets 将数据包通过 PacingController::EnqueuePacket 传入 Pacer 模块。
不止是视频包,对于音频包,重传包,FEC 包等都要统一通过 Pacer 模块发送,不同类型报文会区分优先级。
PacingController 中最重要的成员是 RoundRobinPacketQueue packet_queue,packet_queue 是一个优先级排序的多维队列,其中重要的成员是 std::map<uint32_t, Stream> streams_,即对于相同 SSRC 的包会塞到同一个 Stream 变量中处理,Stream 结构包含了一个 PriorityPacketQueue packet_queue,它是一个按包优先级排序的队列,越优先的包越靠前,能越先发送出去。
包优先级排序规则如下:
\1. 先比较包的 priority 等级,等级越小越优先
\2. 如果 1 判断相等,再判断是否属于重传包,重传包比非重传包优先
\3. 如果 2 判断相等,再判断包进入 Pacer 模块的顺序,例如对于视频包,越早产生的视频帧的包越早进入 Pacer 模块,同一帧的多个包,前面的包会比后面的包越早进入 Pacer 模块