昭通公司做网站,wordpress 远程设置,seo优化运营,wordpress建设的是模板网站吗GoogCcNetworkController 是 GCC 的控制中心#xff0c;它由 RtpTransportControllerSend 通过定时器和 TransportFeedback 来驱动。GoogCcNetworkController 不断更新内部各个组件的状态#xff0c;并协调组件之间相互配合#xff0c;向外输出目标码率等重要参数#xff0…GoogCcNetworkController 是 GCC 的控制中心它由 RtpTransportControllerSend 通过定时器和 TransportFeedback 来驱动。GoogCcNetworkController 不断更新内部各个组件的状态并协调组件之间相互配合向外输出目标码率等重要参数实现拥塞控制功能。
1. 静态结构
GoogCcNetworkController 继承自 NetworkContollerInterface其内部调动了一大堆类来实现拥塞控制包括丢包估计器、延时估计器、带宽探测控制器以及一些码率相关的估计器。
1RtpTransportControllerSend
RtpTransportControllerSend 是 GoogCcNetworkController 的驱动器主要包括定时器和 TranportFeedback 两种驱动同时也是 GoogCcNetworkController 融入 WebRTC 整体架构的接口GoogCcNetworkController 的重要输出包括目标码率、带宽探测、拥塞状态等都是通过 RtpTransportControllerSend 带向其他模块。
2ProbeBitrateEstimator
带宽探测由 GoogCcNetworkController 触发但执行不归 GoogCcNetworkController 管不过带宽探测的结果最终要回到 GoogCcNetworkController 这里来ProbeBitrateEstimator 就是负责带宽探测结果评估的评估后的探测带宽直接交给 GoogCcNetworkController 。
3AlrDetector
GoogCcNetworkController 带宽评估器大部分时候是在链路容量的极限边缘试探这是它们的预设工况。但当发送码率明显小于估计带宽分配带宽这时候工况发生变化WebRTC 需要能检测到这种变化并及时通知相关模块进行调整否则估计值可能会产生很大偏差AlrDetector 就是干这事的。
4CongestionWindowPushbackController
从名字可以看出来与拥塞状态相关。经典的拥塞控制算法一般都会用到拥塞控制窗口比如 TCP 的拥塞控制。WebRTC 的拥塞控制虽然走的不是 TCP 那条路子但还是尊重基于拥塞窗口判断拥塞状态的有效性具体体现是不管你前面耍了多少花活捣鼓了各种估计器最终的估计带宽还是得基于拥塞状态进行调整。CongestionWindowPushbackController 就是就是这个调整器。
5SendBandwidthEstimation
SendBandwidthEstimation 内部包含了基于丢包的带宽估计器LossBasedBweV2 其输出的是综合延时估计和丢包估计的带宽估计值就是最早大家熟知的延时估计和丢包估计两者取其小的逻辑当然最新代码实现远比这个表述复杂。
6ProbeController
GoogCcNetworkController 不直接控制带宽探测带宽探测的发起、参数和冲突处理等都是由 ProbeController 负责GoogCcNetworkController 会更新相关状态和通知相关事件的发生ProbeController 会基于状态和事件来判断是否需要启动带宽探测任务。当然也有主动请求带宽探测的情况比如链路经历一次排空后需要立即进行一次带宽探测。
7AckowledgedBitrateEstimator
深入阅读 GCC 源码就会有一种感觉ACK 码率满天飞。这是因为ACK 码率太重要了它是 GCC 能观测到的唯二带宽另一个是探测码率带宽。作为链路容量最重要的观测值ACK 码率是各大带宽估计器的基础输入。为了彰显尊贵身份针对 ACK 码率的估计祭出了贝叶斯估计器也算是相得益彰。
8DelayBasedBWE
其实GCC 真正的扛把子是延迟带宽估计器看下它后面的小弟就知道了。事实上GCC 最依仗就是延迟估计带宽基于丢包的估计带宽可以认为是一种调整。下面的码率关系图也可以看到延迟估计带宽会作为丢包估计器的一个输入但是延迟估计器不会参考丢包估计带宽。相比最开始的版本DelayBasedBWE 也有很大改变比如卡尔曼滤波没了、引入了基于最小二乘的趋势线判断等具体细节就不展开了。 2. 整体架构
2.1. 在整体架构中的位置
下图描述的是视频处理逻辑架构拥塞控制器 Controller 基于丢包估计器和延时估计器的输出计算得到目标码率 target_bitrate目标码率会传递到带宽分配器 AllocatorAllocator 根据一定算法为编码器和 FEC 分配所需码率。Contoller 会向 Pacer 设置平滑发送码率和链路当前拥塞状态控制平滑发送模块的行为。Controller 自己不会直接发起带宽探测而是通过状态和事件驱动 Prober 适时发起带宽探测。 2.2. 各种码流之间的关系
GoogCcNetworkController 对外输出的码流主要有两个target_bitrate 和 stable_target_bitratetarget_bitrate 由 LossBasedBWE 输出它是综合丢包估计和延迟估计的估计值stable_target_bitrate 由 LinkCapacityTracker 输出是链路容量的一个观测值因为观测值是真实测量得到的所以是 stable 的。CongestionWindowPushbackController 基于网络拥塞状态对 target_bitrate 做了进一步调整输出 pushback_target_bitrate。
ACK 码率是所有估计的基础对不同估计器的其作用机理不同
1对于 LinkCapacityTrackerACK 码率是估计值的上限且只会提高估计值而不会降低估计值。估计值的下限由延迟估计码率决定延迟估计码率只会降低估计值而不会提高估计值。
之所以这么设计是因为 ACK 码率不会超过链路真实容量如果当前 ACK 码率高于之前链路容量的估计值完全有理由使用此 ACK 码率作为当前链路容量的估计值会进行平滑处理但如果 ACK 码率低于当前链路容量估计值不能此 ACK 码率来更新链路容量估计值因为较低的 ACK 码率可能是由一个低于链路容量发送码率导致。
延迟估计带宽是链路容量的一个估计值如果发现延迟估计带宽比上一次的估计值低处于下降趋势中有理由认为延迟估计带宽是在链路容量的极限附近试探如果此次延迟估计带宽低于当前链路容量的估计值那么可以可以用延迟估计带宽更新链路容量估计值。
2对于 DelayBasedBWEACK 码率主要用来控制码率下降或上升的幅度。当链路处于 underusing 或 normal 状态时会用探测码率直接更新估计值。
3对于 LossBasedBWEACK 码率主要用来生成候选者和辅助调整最佳候选者的带宽进而影响最终的估计值。 2.3. 重要调用关系
下图展示了以 GoogCcNetworkController 为核心拥塞控制相关的重要类以及它们之间的调用关系。调用方法后的 F 表示是由 TransportFeedback 驱动的调用调用方法后的 T 表示是由定时器驱动的调用调用方法后的 U 表示是由 MaybeTriggerOnNetworkChanged 驱动的调用。 2.3.1. PacingController
1SetCongested
PacingController 在拥塞状态下会停止发送媒体报文需要继续发送keep-alive报文。如果未确认的报文的大小超出拥塞窗口的大小就认为链路处于拥塞状态未确认报文越多则拥塞越严重。
2SetPacingRates
GoogCcNetworkController 获得新的带宽估计值需要设置到 PacingController用来控制 PacingController 平滑发送速率。包含两个码率PacingRate 和 PaddingRate在讲解平滑发送时再详细说明。
3CreateProbeClusters
如果要发起带宽探测ProbeController 最终会调用 PacingController 的接口创建带宽探测任务其实是间接调用由 RtpTransportControllerSend 完成委托任务。
2.3.2. BitrateAllocator
GoogCcNetworkController 获得新的带宽估计值需要通知 BitrateAllocator 重新进行带宽分配。包含两个码率target_bitrate 和 stable_bitrate在讲解码率分配时再详细说明。
2.3.3. AcknowledgeBitrateEstimator
1IncomingPacketFeedbackVector
AcknowledgeBitrateEstimator 基于 TransportFeedback 计算 ACK 码率。其内部使用数据窗口进行采样每个采样窗口计算一个码率然后使用贝叶斯算法进行平滑。
2SetAlrEndedTime
在进行贝叶斯平滑计算时AcknowledgeBitrateEstimator 会给处于 ALR 状态的样本赋以更低的权重更大的不确定性因为 ALR 状态采集的样本不能反映真实的链路带宽。
2.3.4. CongestionWindowPushbackController
CongestionWindowPushbackController 基于 DataWindow 和 OutstandingData 来判断链路拥塞情况然后根据链路拥塞情况来调整目标码率。
1SetDataWindow
DataWindow 是基于估计带宽和 RTT 计算的理想拥塞窗口大小。因为 WebRTC 应用在实时音视频场景其拥塞窗口大小不能设置的太激进否则可能导致网络管道拥堵延迟增加。
2UpdateOutstandingData
OutstandingData 是观测到的未确认数据的大小。
3UpdatePacingQueue
之所以要获取 PacingQueue 大小是因为 CongestionWindowPushbackController 支持设置实验参数来决定是否将平滑发送模块队列中缓存的数据也记为 OutstandingData。
2.3.5. ProbeBitrateEstimator
ProbeBitrateEstimator 基于 TransportFeedback 来统计探测带宽。
2.3.6. DelayBasedBWE
DelayBasedBWE 依赖 TransportFeedback 计算报文到达延迟差。
2.3.7. ProbeController
1Process
需要循环检查 ALR 状态以启动 ALR 带宽探测任务。
2SetAlrStartTimeMs
设置 ALR 状态开始时间。
3SetAlrEndedTimeMs
设置 ALR 状态结束时间。
4RequestProbe
延迟带宽估计器在收到 TransportFeedback 去更新网络使用状态时如果发现网络使用状态从 underusing 变为 normal说明网络经历了一次排空需要调用 RequestProbe 立即启动一次带宽探测。
5SetEstimatedBitrate
带宽探测完成后需要将探测结果告知 ProbeController一来新的带宽可以用来生成后续目标探测带宽另外对于设置了 probe_further 参数的带宽探测任务可能需要创建进一步的带宽探测任务。
2.3.8. AlrDetector
AlrDetector 基于估计带宽去消耗 budget因此估计带宽变化需要及时通知 AlrDetector否则会导致 ALR 状态会判断错误。
2.3.9. SendSideBandwidthEstimation
1UpdateEstimate
这是一个定时调用。在 LossBasedBWE 还未准备好之前SendSideBandwidthEstimation 需要通过另一套算法来持续更新估计带宽。LossBasedBWE 好以后则是获取 LossBasedBWE 估计结果。同时还需要时刻监视 RTT一旦 RTT 异常需要采取行动降低带宽。
2UpdatePropagationRtt
Propagation RTT 是 RttBasedBackoff 所需用来监控 RTT 异常。
3SetAcknowledgedRate
ACK 码率会通过 OnRateUpdate 传入 LinkCapacityTracker通过 SetAcknowledgedBitrate 传入 LossBasedBWE。LinkCapacityTracker 基于 ACK 码率来跟踪链路容量。LossBasedBWE 中 ACK 码率被作为估计值的下限还有其他很多用途具体参考相关章节。
4UpdateDelayBasedEstimate
延迟带宽估计值会设置到 LinkCapacityTracker 作为估计值的下限。
5UpdateLossbasedEstimate
调用 UpdateBandwidthEstimate 接口将 TransportFeedback 和 延迟估计带宽传入到 LossBasedBWELossBasedBWE 输出的估计带宽会选择丢包估计和延迟估计的较小者。
3. 源码分析
3.1. OnTransportPacketsFeedback
收到 TransportFeedback 会通过层层调用到 GoogCcNetworkController调用流程如下图所示 TransportFeedback 被用来计算延迟梯度和丢包率进而更新延迟带宽估计器和丢包带宽估计器。
NetworkControlUpdate GoogCcNetworkController::OnTransportPacketsFeedback(TransportPacketsFeedback report) {// 判断if (report.packet_feedbacks.empty()) {return NetworkControlUpdate();}// 当前已启用congestion_window_pushback_controller_更新infightif (congestion_window_pushback_controller_) {congestion_window_pushback_controller_-UpdateOutstandingData(report.data_in_flight.bytes());}TimeDelta max_feedback_rtt TimeDelta::MinusInfinity();TimeDelta min_propagation_rtt TimeDelta::PlusInfinity();Timestamp max_recv_time Timestamp::MinusInfinity();// max_recv_time是最及时反馈的数据项std::vectorPacketResult feedbacks report.ReceivedWithSendInfo();for (const auto feedback : feedbacks)max_recv_time std::max(max_recv_time, feedback.receive_time);for (const auto feedback : feedbacks) {// 反馈RTT feedback_ts - send_timeTimeDelta feedback_rtt report.feedback_time - feedback.sent_packet.send_time;// 相对最后一个报文的偏差这个偏差表示收到数据后等待了这么长时间才反馈TimeDelta min_pending_time max_recv_time - feedback.receive_time;// 真实链路RTT需要减去等待处理时间TimeDelta propagation_rtt feedback_rtt - min_pending_time;// 获取最大反馈RTTmax_feedback_rtt std::max(max_feedback_rtt, feedback_rtt);// 获取最小链路RTTmin_propagation_rtt std::min(min_propagation_rtt, propagation_rtt);}if (max_feedback_rtt.IsFinite()) {// 维护feedback_max_rtts_队列feedback_max_rtts_.push_back(max_feedback_rtt.ms());const size_t kMaxFeedbackRttWindow 32;if (feedback_max_rtts_.size() kMaxFeedbackRttWindow)feedback_max_rtts_.pop_front();// 更新链路RTT用来跟踪RTT over limitbandwidth_estimation_-UpdatePropagationRtt(report.feedback_time,min_propagation_rtt);}// 获取ALR状态absl::optionalint64_t alr_start_time alr_detector_-GetApplicationLimitedRegionStartTime();// 退出ALR状态if (previously_in_alr_ !alr_start_time.has_value()) {int64_t now_ms report.feedback_time.ms();acknowledged_bitrate_estimator_-SetAlrEndedTime(report.feedback_time);probe_controller_-SetAlrEndedTimeMs(now_ms);}// 更新ALR状态previously_in_alr_ alr_start_time.has_value();// Ack码率估计acknowledged_bitrate_estimator_-IncomingPacketFeedbackVector(report.SortedByReceiveTime());// 获取ACK码率估计结果auto acknowledged_bitrate acknowledged_bitrate_estimator_-bitrate();// 丢包带宽估计需要ack码率bandwidth_estimation_-SetAcknowledgedRate(acknowledged_bitrate,report.feedback_time);// 识别带宽探测数据包反馈计算探测码率for (const auto feedback : report.SortedByReceiveTime()) {if (feedback.sent_packet.pacing_info.probe_cluster_id !PacedPacketInfo::kNotAProbe) {probe_bitrate_estimator_-HandleProbeAndEstimateBitrate(feedback);}}// 获取探测到的带宽absl::optionalDataRate probe_bitrate probe_bitrate_estimator_-FetchAndResetLastEstimatedBitrate();// 限制探测带宽低于ack码率这样有助于排空网络管道// kProbeDropThroughputFraction 0.85if (limit_probes_lower_than_throughput_estimate_ probe_bitrate acknowledged_bitrate) {DataRate limit std::min(delay_based_bwe_-last_estimate(),*acknowledged_bitrate * kProbeDropThroughputFraction);probe_bitrate std::max(*probe_bitrate, limit);}NetworkControlUpdate update;bool recovered_from_overuse false;// 先更新延迟带宽估计器DelayBasedBwe::Result result;result delay_based_bwe_-IncomingPacketFeedbackVector(report,acknowledged_bitrate,probe_bitrate,estimate_,alr_start_time.has_value());if (result.updated) {if (result.probe) {// 延迟码率是使用探测码率进行更新的则将探测码率作为目标码率bandwidth_estimation_-SetSendBitrate(result.target_bitrate,report.feedback_time);}// 调用了SetSendBitrate还需调用UpdateDelayBasedEstimate进行更新bandwidth_estimation_-UpdateDelayBasedEstimate(report.feedback_time,result.target_bitrate);}// 再更新丢包带宽估计器因为丢包带宽估计器依赖于延迟带宽估计器的结果bandwidth_estimation_-UpdateLossBasedEstimator(report,result.delay_detector_state,probe_bitrate,alr_start_time.has_value());if (result.updated) {// 丢包带宽估计器得到的才是最终带宽评估结果需要将评估结果更新到// ProbeController以防需要进行带宽探测MaybeTriggerOnNetworkChanged(update, report.feedback_time);}recovered_from_overuse result.recovered_from_overuse;// 从OverUse恢复立马触发一次带宽探测if (recovered_from_overuse) {probe_controller_-SetAlrStartTimeMs(alr_start_time);auto probes probe_controller_-RequestProbe(report.feedback_time);update.probe_cluster_configs.insert(update.probe_cluster_configs.end(),probes.begin(), probes.end());}if (rate_control_settings_.UseCongestionWindow() max_feedback_rtt.IsFinite()) {// 根据估计码率和RTT计算并更新current_data_window_UpdateCongestionWindowSize();}if (congestion_window_pushback_controller_ current_data_window_) {// 使用了congestion_window_pushback_controller_则更新计算的拥塞窗口大小congestion_window_pushback_controller_-SetDataWindow(*current_data_window_);} else {// 没有使用congestion_window_pushback_controller_需要将当前计算得到的// 拥塞窗口大小通知给RtpTransportControllerSend然后根据实际inflight判断// 拥塞状态并设置到Pacer进而控制Pacer行为。update.congestion_window current_data_window_;}return update;
}
3.2. OnProcessInterval
GoogCcNetworkController 自己并没有定时器而是通过 RtpTransportControllerSend 来驱动的。 在 OnProcessInterval 方法中SendSideBandwidthEstimation 和 ProbeController 需要定时器进行驱动。
NetworkControlUpdate GoogCcNetworkController::OnProcessInterval(ProcessInterval msg) {NetworkControlUpdate update;// 初始化if (initial_config_) {// 重置参数并设置ProbeController获取带宽探测Cluster配置update.probe_cluster_configs ResetConstraints(initial_config_-constraints);// 更新pacing参数update.pacer_config GetPacingRates(msg.at_time);// 根据配置决定是否启动ALR带宽探测ALR探测是周期探测if (initial_config_-stream_based_config.requests_alr_probing) {probe_controller_-EnablePeriodicAlrProbing(*initial_config_-stream_based_config.requests_alr_probing);}// 设置最大分配码率如果需要探测则添加探测Cluster配置absl::optionalDataRate total_bitrate initial_config_-stream_based_config.max_total_allocated_bitrate;if (total_bitrate) {auto probes probe_controller_-OnMaxTotalAllocatedBitrate(*total_bitrate, msg.at_time);update.probe_cluster_configs.insert(update.probe_cluster_configs.end(),probes.begin(), probes.end());}initial_config_.reset();}// pacer_queue 大小可能被配置为 outstanding dataif (congestion_window_pushback_controller_ msg.pacer_queue) {congestion_window_pushback_controller_-UpdatePacingQueue(msg.pacer_queue-bytes());}// RTT backoff 监控及启动阶段估计值更新bandwidth_estimation_-UpdateEstimate(msg.at_time);// 获取 ALR 状态absl::optionalint64_t start_time_ms alr_detector_-GetApplicationLimitedRegionStartTime();// ProbeController 可能启动了 ALR 带宽探测probe_controller_-SetAlrStartTimeMs(start_time_ms);// 主要用来驱动 ALR 带宽探测auto probes probe_controller_-Process(msg.at_time);// 可能有带宽探测任务update.probe_cluster_configs.insert(update.probe_cluster_configs.end(),probes.begin(), probes.end());// 根据 RTT 和目标码率更新拥塞窗口大小if (rate_control_settings_.UseCongestionWindow() !feedback_max_rtts_.empty()) {UpdateCongestionWindowSize();}// 更新拥塞窗口大小if (congestion_window_pushback_controller_ current_data_window_) {congestion_window_pushback_controller_-SetDataWindow(*current_data_window_);} else {update.congestion_window current_data_window_;}// 检查状态变化MaybeTriggerOnNetworkChanged(update, msg.at_time);return update;
}
3.4. MaybeTriggerOnNetworkChanged
网络状态变化需要及时通知相关利益方以便执行相应处理动作包括触发带宽探测、码率重分配、设置平滑发送码率等。
void GoogCcNetworkController::MaybeTriggerOnNetworkChanged(NetworkControlUpdate* update,Timestamp at_time) {// 丢包率uint8_t fraction_loss bandwidth_estimation_-fraction_loss();// RTTTimeDelta round_trip_time bandwidth_estimation_-round_trip_time();// 丢包估计目标码率综合丢包估计和延迟估计DataRate loss_based_target_rate bandwidth_estimation_-target_rate();// 丢包估计状态LossBasedState loss_based_state bandwidth_estimation_-loss_based_state();// 使用丢包估计目标码率初始化 pushback 目标码率DataRate pushback_target_rate loss_based_target_rate;double cwnd_reduce_ratio 0.0;if (congestion_window_pushback_controller_) {// 传入丢包估计目标码率获取根据拥塞状态调整后的 pushback 码率int64_t pushback_rate congestion_window_pushback_controller_-UpdateTargetBitrate(loss_based_target_rate.bps());// 最小码率约束pushback_rate std::maxint64_t(bandwidth_estimation_-GetMinBitrate(),pushback_rate);// 转换为 DataRatepushback_target_rate DataRate::BitsPerSec(pushback_rate);// 根据拥塞窗口计算后的下调带宽比率if (rate_control_settings_.UseCongestionWindowDropFrameOnly()) {cwnd_reduce_ratio static_castdouble(loss_based_target_rate.bps() -pushback_target_rate.bps()) /loss_based_target_rate.bps();}}// 获取稳定目标码率并用 pushback 目标码率进行了约束DataRate stable_target_rate bandwidth_estimation_-GetEstimatedLinkCapacity();stable_target_rate std::min(stable_target_rate, pushback_target_rate);if ((loss_based_target_rate ! last_loss_based_target_rate_) || // 带宽估计值变化(loss_based_state ! last_loss_base_state_) || // 带宽评估状态变化(fraction_loss ! last_estimated_fraction_loss_) || // 丢包率变化(round_trip_time ! last_estimated_round_trip_time_) || // RTT变化(pushback_target_rate ! last_pushback_target_rate_) || // pushback 码率变化(stable_target_rate ! last_stable_target_rate_)) { // 稳定目标码率变化// 更新last_loss_based_target_rate_ loss_based_target_rate;last_pushback_target_rate_ pushback_target_rate;last_estimated_fraction_loss_ fraction_loss;last_estimated_round_trip_time_ round_trip_time;last_stable_target_rate_ stable_target_rate;last_loss_base_state_ loss_based_state;// 设置ALR探测器估计码率alr_detector_-SetEstimatedBitrate(loss_based_target_rate.bps());TimeDelta bwe_period delay_based_bwe_-GetExpectedBwePeriod();TargetTransferRate target_rate_msg;target_rate_msg.at_time at_time;if (rate_control_settings_.UseCongestionWindowDropFrameOnly()) {// 如果携带cwnd_reduce_ratio,则目标码率为评估的目标码率target_rate_msg.target_rate loss_based_target_rate;target_rate_msg.cwnd_reduce_ratio cwnd_reduce_ratio;} else {// 如果不携带cwnd_reduce_ratio则目标码率为经拥塞窗口调整后的码率target_rate_msg.target_rate pushback_target_rate;}target_rate_msg.stable_target_rate stable_target_rate;target_rate_msg.network_estimate.at_time at_time;target_rate_msg.network_estimate.round_trip_time round_trip_time;target_rate_msg.network_estimate.loss_rate_ratio fraction_loss / 255.0f;target_rate_msg.network_estimate.bwe_period bwe_period;update-target_rate target_rate_msg;// 更新带宽探测控制器的估计码率auto probes probe_controller_-SetEstimatedBitrate(loss_based_target_rate,GetBandwidthLimitedCause(bandwidth_estimation_-loss_based_state(),bandwidth_estimation_-IsRttAboveLimit(),delay_based_bwe_-last_state()),at_time);// 添加带宽探测 Cluste r配置update-probe_cluster_configs.insert(update-probe_cluster_configs.end(),probes.begin(), probes.end());// 更新 Pacing 参数update-pacer_config GetPacingRates(at_time);}
}
4. 总结
简单总结下 WebRTC 拥塞控制思路。拥塞控制的核心是获取链路的带宽对于实时音视频通信来说还要考虑延时指标。因为只有获得了链路的真实带宽才能确保发送的码率不会超过链路容量从而避免产生拥塞。那有没有一种办法在不发送码流的情况下提前知道链路的真实带宽呢答案是没有。这个问题貌似变成了一个先有鸡还是先有蛋的问题。理论上是这样的但实践中可以采用一个带有负反馈回路的控制算法来打破这个循环魔咒这就是 WebRTC 拥塞控制的核心思想。
我们回过头来总结 GCC 实现的底层逻辑设置一个起始码率按照起始码率开始发送报文通过 RTCP 收集各种反馈将反馈导入估计器延时带宽估计器、丢包带宽估计器以及其他一堆估计器产生估计值调整发送码率按照新的目标码率发送报文继续收集反馈产生新的估计值形成循环反馈回路。
这样就完了吗并没有如何产生更可靠估计值成了控制算法的关键问题。为了更准确、更可靠的估计链路容量WebRTC 采用了延时带宽估计器和丢包带宽估计器两个估计器同时还引入 RTT、ACK 码率、探测码率、链路容量等一系列估计值来对估计结果进行调整和修正有一大坨代码都是在解决如果获得更好估计这个问题。如果要深刻理解 WebRTC 是如何解决这个问题的就必须深入算法和模块细节比如延迟估计中是如何实现网络状态检测的丢包估计中是如何实现最优参数搜索的等。理解了必要的细节后再跳脱出来将这些模块和算法联系起来将一个个逻辑链条组成前面提到的基于反馈回路的控制算法逻辑。
必须得承认GCC 算法整体是非常优秀的主要表现在以下几个方面
1良好的顶层架构
模块职责划分清晰模块接口和模块之间的关系设计合理使得 GCC 能够很好的融入 WebRTC 整体架构。
2坚实的理论基础
基于延迟梯度的拥塞控制算法在学术界有很多研究成果基于链路固有丢包率属性和二项分布丢包模型设计精巧的目标函数通过收集观测样本并搜索最优参数组合算法整体有很好的数学理论支撑。
3大量使用成熟算法
基本上每个估计器都有经典算法的加持比如指数加权移动平均算法、贝叶斯估计算法、最小二乘线性拟合算法等。
不过GCC 经过多年的演化也有几个问题值得探讨
1GCC 的实现越来越复杂这种复杂性目前主要体现在子模块内部但也有向上蔓延的趋势子模块之间的耦合越来越强。GCC 当前的实现相比他们第一次发表的论文做了很多改进和优化甚至一些关键逻辑有了很大变化导致的现象是分支和判断越来越多复杂度不断加码有一种不断往之前算法框架上打补丁的既视感。
2GCC 里面涉及到一堆估计器这些估计器的融合看起来缺少理论基础。比如延迟估计和丢包估计两者取其小一定是合理的吗ACK 码率、探测码率、延迟估计码率、丢包估计码率、pushback 码率、链路容量等这些概念相互交织相关限制和调整逻辑看似合理但缺少足够的说服力。
3引入大量模型参数导致模型理解困难实现复杂。比如 LossBasedBweV2::Config足足有 39 个配置参数这对于模型调优来说简直就是噩梦。根据奥卡姆剃刀原理这么复杂的模型可能是脆弱的不必要的有必要在更高维度上进行抽象和提炼并进行适当简化。