FecControllerPlrBased 模块是 WebRTC 音频网络适配器Audio Network Adaptor, ANA中的一个控制模块。它的核心职责是根据网络丢包率Packet Loss Rate, PLR和上行带宽动态决定是否启用前向纠错FEC。FEC 是一种通过发送冗余数据来对抗网络丢包的技术但它会占用额外的带宽。因此需要在“抗丢包能力”和“带宽消耗”之间取得平衡。这个类就是用来做这个权衡决策的。该模块通过这种动态、滞后的策略确保了在网络波动时音频质量的稳定性同时避免了不必要的带宽浪费。一、滞后阈值Hysteresis为了防止 FEC 状态在“开启”和“关闭”之间频繁震荡Flapping该类使用了两条不同的阈值曲线形成一种滞后机制1 fec_enabling_threshold (开启阈值):• 当当前丢包率 高于 这条曲线时决定开启 FEC。• 这条曲线通常较高意味着只有当网络状况较差丢包多且带宽允许时才愿意付出带宽代价去开启 FEC。2 fec_disabling_threshold (关闭阈值):• 当当前丢包率 低于 这条曲线时决定关闭 FEC。• 这条曲线通常较低意味着只有当网络状况明显好转丢包很少时才关闭 FEC 以节省带宽。3说明packet-loss ^ | | | | | FEC ON (区域) | \ \ | FEC \ \_______ fec_enabling_threshold (高阈值) | OFF \_________ fec_disabling_threshold (低阈值) |----------------- bandwidth• 如果当前状态是 FEC OFF必须等到丢包率超过上方的 enabling 曲线才会切换为 ON。• 如果当前状态是 FEC ON必须等到丢包率降到下方的 disabling 曲线以下才会切换为 OFF。• 在两条曲线之间的区域保持当前状态不变。二、关键成员变量2.1 config:• 存储配置信息包括初始状态、两条阈值曲线以及平滑滤波器的时间常数。struct Config { // |fec_enabling_threshold| defines a curve, above which FEC should be // enabled. |fec_disabling_threshold| defines a curve, under which FEC // should be disabled. See below // // packet-loss ^ | | // | | | FEC // | \ \ ON // | FEC \ \_______ fec_enabling_threshold // | OFF \_________ fec_disabling_threshold // |----------------- bandwidth Config(bool initial_fec_enabled, const ThresholdCurve fec_enabling_threshold, const ThresholdCurve fec_disabling_threshold, int time_constant_ms); bool initial_fec_enabled; ThresholdCurve fec_enabling_threshold; ThresholdCurve fec_disabling_threshold; int time_constant_ms; };2.2 fec_enabled_• 当前 FEC 的状态标志true/false。2.3 uplink_bandwidth_bps_• 当前的估计上行带宽。阈值曲线是带宽的函数因为带宽越高我们越能承受 FEC 带来的额外开销。2.4 packet_loss_smoother_ (SmoothingFilter)• 平滑滤波器。网络报告的丢包率通常是瞬时且波动的。直接使用原始丢包率会导致决策不稳定。• 这个滤波器对丢包率进行指数加权移动平均EWMA提供一个更平滑、更稳定的丢包率估值用于决策。• time_constant_ms 决定了平滑的程度时间常数越大反应越慢但越稳定时间常数越小反应越快但易受噪声影响。三 主要方法3.1 UpdateNetworkMetrics(const NetworkMetrics network_metrics):• 输入更新。从 ANA 的其他部分接收最新的网络统计数据。• 提取上行带宽 (uplink_bandwidth_bps)。• 提取原始丢包率并通过 packet_loss_smoother_ 进行平滑处理得到用于决策的有效丢包率。3.2 MakeDecision(AudioEncoderRuntimeConfig* config):• 核心决策逻辑。这是 Controller 接口的实现每帧或定期被调用。• 它会根据当前的平滑丢包率和带宽查询两条阈值曲线。• 调用 FecEnablingDecision 或 FecDisablingDecision 来判断是否应该改变 fec_enabled_ 的状态。• 最终将决策结果写入 config-enable_fec从而指导音频编码器是否生成 FEC 数据。3.3 FecEnablingDecision / FecDisablingDecision:• 私有辅助函数分别执行具体的比较逻辑1 FecEnablingDecision: 如果 !fec_enabled_ 且 smoothed_packet_loss GetThreshold(bandwidth, enabling_curve)则返回 true。2 FecDisablingDecision: 如果 fec_enabled_ 且 smoothed_packet_loss GetThreshold(bandwidth, disabling_curve)则返回 true。四、工作流程1. 初始化: 创建实例设定初始 FEC 状态和阈值曲线。2. 监控: 每次收到网络指标更新 (UpdateNetworkMetrics)更新带宽估计并平滑最新的丢包率样本。3. 决策: 在 MakeDecision 中• 根据当前带宽从 fec_enabling_threshold 曲线计算出“开启临界丢包率”。• 根据当前带宽从 fec_disabling_threshold 曲线计算出“关闭临界丢包率”。• 如果当前 FEC 是关闭的且平滑丢包率 开启临界值 - 开启 FEC。• 如果当前 FEC 是开启的且平滑丢包率 关闭临界值 - 关闭 FEC。• 否则保持现状。4. 执行: 将结果传递给音频编码器编码器据此调整输出包的格式是否包含冗余帧。五为什么需要基于带宽的曲线FEC 会增加比特率。• 低带宽时即使丢包率稍高也可能无法承受 FEC 带来的额外负载否则会导致拥塞加剧。因此低带宽下的开启阈值会设得很高很难开启 FEC。• 高带宽时有足够的余量容纳冗余数据因此可以更早地开启 FEC 以保护音质。此时开启阈值会降低。