webrtc 音频模块FEC模块
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 以保护音质。此时开启阈值会降低。
