拥塞控制对于不同网络条件下保证音视频传输质量非常重要。mediasoup 移植了 WebRTC 的 GCC 模块,嵌入到服务器,使得 mediasoup 具备了和 WebRTC 客户端一样的拥塞控制能力。为了使 GCC 能够与 mediasoup 框架良好交互,mediasoup 做了很多适配工作,包括如何驱动 GCC 以及如何使用 GCC 输出,本文主要分析这部分的设计与实现。
1. 方案分析
在《深入浅出WebRTC-GCC》中有讲过,GCC 实现非常复杂,但 GCC 是通过 RtpTransportControllerSend 嵌入到 WebRTC 整体框架中的,因此,集成 GCC 只需要与 RtpTransportControllerSend 交互即可,这就大大降低了集成的复杂度。
mediasoup 源码中 RtpTransportControllerSend 接口如下。RtpTransportControllerSend 接口可以分为 3 类:设置、报文和定时器。码率、网络、ALR、平滑增益等属于设置类接口;发送报文、接收 RR、接收 TransportFeedback 等属于报文类接口;最后再加上一个定时器驱动的 Process 接口。
// 设置分配的全局发送码率限制,内会设置带宽探测和平滑发送模块参数
void SetAllocatedSendBitrateLimits(int min_send_bitrate_bps,
int max_padding_bitrate_bps,
int max_total_bitrate_bps) override;
// 设置码率偏好[min, start, max],内部会设置带宽评估器参数
void SetClientBitratePreferences(const TargetRateConstraints& constraints);
// 设置平滑增益
void SetPacingFactor(float pacing_factor) override;
// 网络连接和断开
void OnNetworkAvailability(bool network_available) override;
// 是否设置周期性 ALR 探测
void EnablePeriodicAlrProbing(bool enable) override;
// 报文发送到网络后回调
void OnSentPacket(const rtc::SentPacket& sent_packet, size_t size) override;
// 这个接口可以不用关注(默认没有变化)
void OnTransportOverheadChanged(
size_t transport_overhead_per_packet) override;
// 这个接口不用关注(这是 REMB 实现,我们只分析 TCC 实现)
void OnReceivedEstimatedBitrate(uint32_t bitrate) override;
// 收到 RTCP RR 处理,RTT 以及统计的丢包需要更新到 GCC
void OnReceivedRtcpReceiverReport(const ReportBlockList& report_blocks,
int64_t rtt,
int64_t now_ms) override;
// 报文发送要调用到这里,内部会保存发送报文信息
void OnAddPacket(const RtpPacketSendInfo& packet_info) override;
// 收到 TransportFeedback 处理,内部会关联之前保存的发送报文,并传递到 GCC 处理
void OnTransportFeedback(const RTC::RTCP::FeedbackRtpTransportPacket& feedback) override;
// 定时器驱动
void Process();
是不是适配 RtpTransportControllerSend 就可以了呢?还不行,还要考虑平滑发送模块的适配。mediasoup 没有实现平滑发送,原因是 mediasoup 作为一个媒体转发服务器, 主要任务是转发数据包,调整数据包的发送速率依赖于 WebRTC 客户端的能力。又因为平滑发送模块与带宽探测功能联系在一起,为此,mediasoup 重新实现了 PacedSender,保留了带宽探测能力。
【注】这里重点说明下,我理解,mediasoup 之所以没有实现平滑发包,是因为其只实现了 NACK,没有实现 FEC。如果实现 FEC 的话,则需要在服务端进行 FEC 解码和组帧,然后再重新做 FEC 编码并发送,此时服务端发送的行为和客户端是一致的,都是发送视频帧,平滑发送是必须的。
2. 静态结构
GCC 功能实现需要发送端和接收端的配合,新版本使用 TransportCC 代替了之前的 REMB 方案。TransportCC 方案绝大部分计算都放在了发送端,因此,本文只讨论发送端的实现。
mediasoup 使用 TransportCongestionControlClient 来实现 GCC 发送端的功能,如下图所示。TransportCongestionControlClient 作为 mediasoup 与 GCC 之间的适配层,它继承了 webrtc::PacketRouter 接口,用生成和发送探测报文;继承了 webrtc::TargetTransferRateObserver, 用来接收 GCC 的估