在webrtc弱网整个链路中,存在这么一个模块,发送数据时采取有节奏的平滑发送,这个模块就是PacingController类。
一、核心功能
-
流量整形:基于漏桶算法控制数据包发送速率
-
优先级调度:音频 > 重传 > 视频 > 填充的优先级处理
-
带宽探测:通过 BitrateProber 实现主动带宽探测
-
拥塞响应:根据网络状态动态调整发送行为
-
队列管理:监控队列长度,动态提速防止积压
-
统计监控:实时统计各类数据包发送速率
二、核心算法原理
漏桶算法实现:
// 媒体负债更新逻辑
void UpdateBudgetWithElapsedTime(TimeDelta delta) {
// 随时间减少负债(漏水过程)
media_debt_ -= min(media_debt_, adjusted_media_rate_ * delta);
padding_debt_ -= min(padding_debt_, padding_rate_ * delta);
}
三、关键数据结构
// 发送统计结构
struct PacerPacketStat {
size_t rtx_bytes_sent = 0; // 重传包字节数
size_t fec_bytes_sent = 0; // FEC包字节数
size_t media_bytes_sent = 0; // 媒体包总字节数
size_t media_bytes_sent_only_video = 0; // 视频媒体包字节数
};
// 优先级队列(核心排序逻辑)
class PrioritizedPacketQueue {
// 优先级顺序:音频 > 重传 > 视频 > 填充
std::priority_queue<...> queue_;
};
四、核心方法详解
1.入队控制 (EnqueuePacket)
void EnqueuePacket(std::unique_ptr<RtpPacketToSend> packet) {
// 关键帧刷新:清空队列保证关键帧优先
if (keyframe_flushing_ && packet->is_key_frame()) {
packet_queue_.RemovePacketsForSsrc(packet->Ssrc());
}
// 通知探测器新包到达
prober_.OnIncomingPacket(DataSize::Bytes(packet->payload_size()));
// 入队并更新状态
packet_queue_.Push(CurrentTime(), std::move(packet));
}
2.发送引擎 (ProcessPackets)
void ProcessPackets() { // 1. 保活包发送(防止连接冻结) if (ShouldSendKeepalive(now)) { SendPadding(DataSize::Bytes(1)); } // 2. 带宽探测处理 if (prober_.is_probing()) { DataSize probe_size = prober_.RecommendedMinProbeSize(); SendProbePackets(probe_size); } // 3. 主发送循环(带熔断保护) while (!circuit_breaker_triggered) { // 4. 获取待发送包(优先级顺序) auto packet = GetPendingPacket(); if (packet) { // 5. 发送并更新负债 SendMediaPacket(std::move(packet)); } else if (CanSendPadding()) { // 6. 发送填充包 SendGeneratedPadding(); } } // 7. 状态更新 UpdateBitrateStat(); MaybeUpdateMediaRateDueToLongQueue(); }
3.动态速率调整
void MaybeUpdateMediaRateDueToLongQueue() { // 计算队列预计消耗时间 TimeDelta avg_time_left = queue_time_limit_ - packet_queue_.AverageQueueTime(); // 动态提速逻辑 if (avg_time_left < kMinQueueThreshold) { // 需要的最小速率 = 队列大小 / 剩余时间 DataRate min_rate_needed = queue_size_data / avg_time_left; adjusted_media_rate_ = min_rate_needed; } }
五、设计亮点
-
多级优先级系统:确保音频和重传包绝对优先
-
动态速率调整:基于队列长度自动提速防止积压
-
关键帧刷新:清空队列保证关键帧低延迟
-
熔断机制:
circuit_breaker_threshold_
防止死循环 -
精确时间控制:
kMaxEarlyProbeProcessing
控制探测包发送时机 -
保活机制:网络空闲时定期发送1字节填充包维持连接
六、典型工作流程
七、核心负债机制详解
// 媒体负债(media_debt_): // - 发送媒体包时增加:media_debt_ += sent_bytes // - 随时间减少:media_debt_ -= min(media_debt_, rate * elapsed_time) // - 上限控制:不超过adjusted_media_rate_ * kMaxDebtInTime // 填充负债(padding_debt_): // - 发送填充包时增加 // - 随时间减少(类似媒体负债) // - 用于控制填充包发送频率 // 负债更新示例: void OnPacketSent(RtpPacketMediaType type, DataSize size) { if (type != RtpPacketMediaType::kPadding) { media_debt_ += size; // 发送媒体包增加负债 } padding_debt_ += size; // 所有发送都增加填充负债 }
总结
PacingController 作为 WebRTC 的流量整形引擎,通过负债机制实现:
-
平滑发送:漏桶算法控制发送速率
-
智能调度:四级优先级保障关键数据
-
动态适应:队列监控自动提速
-
网络探测:主动带宽测量
-
拥塞响应:实时调整发送策略
其精妙的负债管理和时间控制机制(关键变量 media_debt_
/padding_debt_
)使其能在复杂网络环境中平衡传输效率与网络友好性,是 WebRTC 传输系统的核心组件。