UlpfecGenerator 是 WebRTC 中用于生成 ULPFEC (Uneven Level Protection Forward Error Correction) 冗余包的核心类。
它实现了 VideoFecGenerator 接口,主要负责根据视频帧的重要性(关键帧 vs 差值帧)和当前的网络状况,动态计算并生成前向纠错码(FEC),并将这些 FEC 数据封装在 RED (Redundant Audio Data) 格式或直接作为独立的 FEC 包发送,以对抗网络丢包。
UlpfecGenerator
是 WebRTC 视频发送端传统 FEC 方案的实现者。它智能地平衡了抗丢包能力和带宽开销,通过对关键帧和差值帧的区别对待,以及动态调整生成时机,确保了在弱网环境下视频通话的流畅性和清晰度。
一、核心职责
-
FEC 生成引擎:接收原始的媒体 RTP 包,缓冲它们,并在满足特定条件(如帧结束、达到最小包数)时,调用底层的纠删码算法生成冗余包。
-
不等保护 (UEP):支持对关键帧(Keyframe)和差值帧(Delta frame/P-frame)应用不同的保护强度。通常关键帧更重要,因此会分配更高的 FEC 率。
-
RED 封装支持:ULPFEC 通常与 RED payload 格式结合使用。生成的 FEC 包需要知道 RED 的 Payload Type,以便接收端正确解析。
二、关键成员变量
cpp
const int red_payload_type_;
const int ulpfec_payload_type_;
Clock* const clock_;
rtc::RaceChecker race_checker_;
const std::unique_ptr<ForwardErrorCorrection> fec_
RTC_GUARDED_BY(race_checker_);
ForwardErrorCorrection::PacketList media_packets_
RTC_GUARDED_BY(race_checker_);
absl::optional<RtpPacketToSend> last_media_packet_
RTC_GUARDED_BY(race_checker_);
std::list<ForwardErrorCorrection::Packet*> generated_fec_packets_
RTC_GUARDED_BY(race_checker_);
int num_protected_frames_ RTC_GUARDED_BY(race_checker_);
int min_num_media_packets_ RTC_GUARDED_BY(race_checker_);
Params current_params_ RTC_GUARDED_BY(race_checker_);
bool keyframe_in_process_ RTC_GUARDED_BY(race_checker_);
rtc::CriticalSection crit_;
absl::optional<Params> pending_params_ RTC_GUARDED_BY(crit_);
RateStatistics fec_bitrate_ RTC_GUARDED_BY(crit_);
2.1 配置与标识
• red_payload_type: RED 格式的 RTP Payload Type。
• ulpfec_payload_type: ULPFEC 包的 RTP Payload Type。
• fec (std::unique_ptr<ForwardErrorCorrection>):
• 这是实际的纠删码算法实现对象。UlpfecGenerator 本身不执行 XOR 或矩阵运算,而是委托给 fec_ 对象。它负责管理数据包列表并触发 fec_->EncodeFec()。
2.2 状态管理
• media_packets_: 当前正在累积的媒体包列表。这些包将作为生成 FEC 的源数据。
• generated_fec_packets_: 已经生成但尚未被上层取走的 FEC 包列表。
• current_params_: 当前的保护参数,包含针对 Delta 帧和 Key 帧的 FecProtectionParams(如 fec_rate, max_fec_frames 等)。
• keyframe_in_process_: 标志位,指示当前是否正在处理一个关键帧。这决定了使用哪一组保护参数(keyframe_params 还是 delta_params)。
• num_protected_frames_: 当前 FEC 块中已保护的帧数。用于判断是否达到了 max_fec_frames 限制,从而触发 FEC 生成。
• min_num_media_packets_: 触发 FEC 生成所需的最小媒体包数量。如果包太少,生成 FEC 的效率很低(开销占比过大),因此会等待更多包。
2.3 统计
• fec_bitrate_: 统计当前 FEC 生成的比特率,用于监控带宽开销。
三,调用流程
3.1. 设置参数 (SetProtectionParameters):
• 上层(如 VideoSender 或 FlexfecSender)根据带宽估计和丢包率,调用此方法设置新的保护策略。
• 参数包括 Delta 帧和 Key 帧的目标 FEC 率、最大保护帧数等。
3.2. 添加媒体包 (AddPacketAndGenerateFec):
• 当视频编码器产生一个 RTP 包时,调用此方法。
• 缓冲:将包添加到 media_packets_ 列表。
• 状态更新:检测是否是关键帧的开始(通过 Marker Bit 或内部逻辑),更新 keyframe_in_process_。
• 触发判断:检查是否满足生成 FEC 的条件:
• 是否达到了最小媒体包数 (MinimumMediaPacketsReached)?
• 是否达到了最大保护帧数或时间窗口?
• 当前帧是否结束(Marker Bit set)?
• 生成:如果条件满足,调用 fec_->EncodeFec()。这会遍历 media_packets_,计算冗余数据,生成一个或多个 FEC 包,并存入 generated_fec_packets_。
• 重置:清空 media_packets_,准备下一批次的累积。
3.3. 获取 FEC 包 (GetFecPackets):
• 上层发送线程调用此方法,取出所有已生成的 FEC 包。
• 返回的包是完整的 RtpPacketToSend 对象,包含正确的 RTP 头(SSRC, SeqNum, Timestamp 等)和 ULPFEC/RED 载荷。
3.4. 开销计算 (MaxPacketOverhead, Overhead):
• 计算 FEC 带来的额外带宽消耗。这对于带宽控制器(Bandwidth Controller)至关重要,因为它需要从总带宽预算中扣除 FEC 占用的部分,剩下的才用于发送视频内容。
四、其它辅助功能
4.1 ExcessOverheadBelowMax:
• FEC 率是一个目标值(例如 20%)。但在包数较少时,由于离散性,实际生成的 FEC 包比例可能会高于目标值(例如 1 个媒体包生成 1 个 FEC 包,开销 100%)。
• 此函数检查实际开销是否超出了允许的最大偏差。如果超出,可能会推迟生成或调整策略,以避免在网络拥塞时雪上加霜。
4.2 MinimumMediaPacketsReached:
为了防止"小帧大开销"的问题。如果一帧只有 1-2 个包,生成 FEC 的性价比极低。此逻辑确保只有当累积了足够多的媒体包时,才启动 FEC 编码过程。
五 与 FlexFEC 的关系
1, 代码中声明 friend class FlexfecSender;。,
2, 在 FlexfecSender 的解释中提到的FlexfecSender 内部复用了 UlpfecGenerator 的逻辑。虽然 FlexFEC 协议不同,但底层的"累积包 -> 触发编码 -> 生成冗余"的状态机和管理逻辑是非常相似的。FlexfecSender 会调用 UlpfecGenerator 的内部方法来利用其成熟的包管理功能,尽管最终生成的包格式不同。