ARQ与FEC混合方案

在底层 UDP 传输协议中,单纯依靠 FEC(前向纠错)会浪费带宽,单纯依靠 ARQ(丢包重传)会带来巨大的延迟。ARQ+FEC 混合方案(Hybrid ARQ) 是目前实时音视频通信(如 WebRTC、Zoom)的主流方案。

以下是一个完整的底层实现逻辑框架:


1. 整体架构设计

混合方案的核心逻辑是:用 FEC 覆盖随机的小量丢包(零延迟),用 ARQ 覆盖大面积的丢包或突发丢包(低带宽消耗)。


2. 发送端 (Sender) 实现流程

发送端需要维护一个滑动窗口(Sliding Window),用于存储已发送但尚未确认的包。

A. 封包与缓存

  • 给每个包打上全局唯一的序列号 (SN)
  • 将原始音频包放入 Send_Window 缓存,等待可能的 ARQ 请求。

B. FEC 编码策略

  • 分组 (Group):每K个原始包组成一个矩阵。
  • 计算 (Encode):生成M个冗余包(通常使用 Reed-Solomon 算法)。
  • 动态冗余率:根据接收端反馈的丢包率 L动态调整M的值。
  • 若 ‘L < 5%’ M=1(低冗余)。
  • 若 ‘L > 15%’ M =K*0.4(高冗余)。

C. 响应 ARQ 请求

  • 监听来自接收端的 NACK (Negative Acknowledgment) 包。
  • Send_Window 中检索指定的 SN,重新打包发送。
  • 优先级控制:ARQ 的重传包优先级应高于新的 FEC 包。

3. 接收端 (Receiver) 实现流程

接收端的核心是 Jitter Buffer(抖动缓冲区),它负责排序、解码和触发请求。

A. 处理逻辑流水线

  1. 收包入库:收到包后根据 SN 放入 Buffer。
  2. FEC 尝试(即时)
  3. 检查当前组 (Group) 是否有空缺。
  4. 如果缺失的包数小于收到冗余包数,立即执行矩阵解码,恢复原始包。

  5. ARQ 触发(定时/间隙)

  6. 如果 FEC 无法恢复(丢包太多),且时间允许(尚未到达播放时间),发送 NACK 给发送端。

  7. 递交给解码器

  8. 按 SN 顺序取出包,交给 Opus/AAC 解码。

4. 关键算法与协议细节

数据包结构定义 (C++)

enum PacketType { MEDIA = 0, FEC = 1, NACK = 2 };

struct Packet {
    uint16_t sn;        // 序列号
    uint8_t  type;      // 包类型
    uint16_t group_id;  // FEC 组 ID
    uint32_t ts;        // 时间戳(用于计算抖动)
    uint8_t  payload[MAX_SIZE];
};

ARQ 的“限制性策略”

在实时音频中,不能无限次重传。

  • RTT 判定:如果 (当前时间 – 包发送时间) > 2.5倍 RTT,通常放弃重传。
  • 过期判定:如果包的 SN 已经落后于当前播放指针,直接丢弃 NACK 请求。

5. ARQ 与 FEC 的协同触发算法

如何平衡两者?这里推荐一个常用的工业级逻辑:

丢包情况 处理策略 理由
单随机丢包 FEC 恢复 延迟为 0,不产生额外往返时间。
突发丢包 (Burst) ARQ 请求 FEC 难以覆盖连续 10 个以上的丢包,重传更节省带宽。
极高 RTT 网络 加大 FEC 权重 RTT 太大(如 >300ms)时,ARQ 重传回来也过时了,只能靠 FEC 硬扛。
极低带宽网络 减小 FEC,开启 ARQ FEC 产生的冗余流量会加剧拥塞,ARQ 按需索取更精准。

6. 实现建议

  1. 引入 RTT 估算:你需要实现类似 TCP 的 RTT 采样(通过发送包和接收 ACK 的时间差),这是判断 ARQ 是否还有效的前提。
  2. 使用成熟的 RS 库:推荐使用 multi-platform FEC library (如 OpenFEC) 或 Jerasure 库处理矩阵运算,不要手写有限域算术。
  3. 带宽估算 (BWE):如果可能,集成 GCC (Google Congestion Control) 算法,它能告诉你当前网络能跑多少码率,从而决定音频码率 + FEC 冗余量的总和。

WebRTC 的 ARQ 和 FEC 实现机制

WebRTC 是 ARQ+FEC 混合方案的典型实现,它综合使用了多种丢包恢复技术,实现了业界领先的实时通信质量。

WebRTC ARQ 实现

RTP NACK(Negative Acknowledgment)

实现机制
协议基础:基于 RTP/RTCP 协议(RFC 3550、RFC 4585)
丢包检测:接收端通过序列号间隙检测丢包
反馈机制:通过 RTCP Feedback 包(RTCP NACK)通知发送端
批量请求:一次 NACK 可以请求多个丢失的包(序列号范围)

关键参数
NACK 间隔:通常每 5-20ms 发送一次 NACK
最大重传次数:每个包最多重传 2-3 次
RTT 阈值:如果 RTT > 200ms,降低 NACK 频率或禁用

实现细节

// WebRTC NACK 包结构(简化)
struct RTCPNACK {
    uint32_t sender_ssrc;      // 发送端 SSRC
    uint32_t media_ssrc;        // 媒体流 SSRC
    uint16_t pid;              // 第一个丢失包的序列号
    uint16_t blp;              // Bitmask,表示后续丢失的包
};

RTX(RTP Retransmission)

实现机制
独立流:使用独立的 RTP 流(RTX 流)传输重传包
SSRC 分离:原始流和重传流使用不同的 SSRC
序列号映射:重传包使用 RTX 序列号,通过 RTP Header Extension 携带原始序列号

优势
– 可以区分原始包和重传包,便于统计和监控
– 支持不同的传输优先级
– 兼容性更好,符合 RTP 标准

实现细节

// RTX 包结构
struct RTXPacket {
    uint16_t rtx_seq;          // RTX 流序列号
    uint16_t original_seq;     // 原始序列号(通过 Header Extension)
    uint8_t  payload[];
};

NACK 触发策略

触发条件
1. 序列号间隙检测:发现序列号不连续
2. 定时触发:每 5-20ms 检查一次,批量发送 NACK
3. RTT 判断:如果 RTT 过大,可能放弃 NACK

优化策略
延迟发送:等待一小段时间(5-10ms),可能收到乱序包
批量处理:一次 NACK 请求多个丢失的包
优先级控制:关键帧(I帧)优先重传

WebRTC FEC 实现

ULPFEC(Uneven Level Protection FEC)

实现机制
协议标准:RFC 5109
编码算法:基于 XOR 运算的简单 FEC
保护模式:可以保护多个 RTP 包(通常 2-8 个)
多级保护:不同重要性的包可以使用不同的 FEC 保护级别

编码过程
1. 分组:将连续的 K 个媒体包分为一组
2. 编码:对这 K 个包进行 XOR 运算,生成 FEC 包
3. 发送:媒体包和 FEC 包一起发送

解码过程
1. 检测丢包:检查组内是否有包丢失
2. 判断可恢复性:如果丢失包数 ≤ FEC 包数,可以恢复
3. XOR 解码:使用收到的包和 FEC 包进行 XOR 运算恢复丢失的包

关键参数
保护包数:通常保护 2-8 个包
冗余率:根据丢包率动态调整(5%-40%)
延迟:零延迟,无需等待往返时间

FlexFEC

实现机制
协议标准:RFC 8627
编码算法:支持多种编码方式(XOR、Reed-Solomon 等)
保护模式:支持 1D(一维)和 2D(二维)保护模式
灵活性:可以灵活配置保护参数

1D 保护模式
– 对连续的包进行一维保护
– 适合随机丢包场景

2D 保护模式
– 对包矩阵进行二维保护
– 可以同时保护行和列
– 适合突发丢包场景

配置示例

// FlexFEC 配置
struct FlexFECConfig {
    uint8_t  protection_mode;   // 1D 或 2D
    uint8_t  row_k;              // 行保护包数
    uint8_t  row_n;              // 行总包数
    uint8_t  col_k;              // 列保护包数(2D模式)
    uint8_t  col_n;              // 列总包数(2D模式)
};

WebRTC 协同机制

自适应策略

WebRTC 根据网络状况动态调整 ARQ 和 FEC 的使用:

网络质量好(丢包率 < 2%)
– 降低 FEC 冗余度(5-10%)
– 主要依赖 NACK 重传
– 减少带宽开销

网络质量中等(丢包率 2-10%)
– 中等 FEC 冗余度(15-25%)
– NACK + FEC 组合使用
– 平衡延迟和带宽

网络质量差(丢包率 > 10%)
– 提高 FEC 冗余度(30-40%)
– 如果 RTT 高,减少 NACK 依赖
– 优先保证流畅度

RTT 自适应

低 RTT(< 50ms)
– 优先使用 NACK,延迟低
– FEC 冗余度较低

中等 RTT(50-200ms)
– NACK + FEC 组合
– 根据 RTT 调整 NACK 超时时间

高 RTT(> 200ms)
– 主要依赖 FEC
– 减少或禁用 NACK(重传回来也过时了)

带宽自适应

带宽充足
– 可以使用较高的 FEC 冗余度
– 保证更好的质量

带宽受限
– 降低 FEC 冗余度
– 优先使用 NACK(按需重传)
– 根据带宽估算(BWE)调整码率和冗余度

WebRTC 关键实现细节

发送端实现

包发送流程
1. 媒体包发送:发送原始媒体包,记录到发送窗口
2. FEC 编码:对媒体包进行 FEC 编码,生成冗余包
3. FEC 包发送:发送 FEC 包
4. NACK 处理:收到 NACK 后,通过 RTX 流重传

优先级控制
– RTX 重传包优先级最高
– 关键帧(I帧)优先级高于普通帧
– FEC 包优先级低于媒体包

接收端实现

包接收流程
1. 包接收:收到包后放入 Jitter Buffer
2. FEC 尝试:立即尝试使用 FEC 恢复丢包
3. NACK 触发:如果 FEC 无法恢复,且时间允许,发送 NACK
4. 包排序:按序列号排序,递交给解码器

Jitter Buffer 管理
最小延迟:通常 30-50ms
最大延迟:根据网络状况动态调整(50-200ms)
包过期:超过播放时间的包直接丢弃

统计和反馈

关键指标
丢包率:通过 RTCP Receiver Report 反馈
RTT:通过 RTCP Sender Report 和 Receiver Report 计算
带宽估算:使用 GCC(Google Congestion Control)算法

反馈机制
RTCP RR:接收端报告,包含丢包率、延迟等信息
RTCP NACK:丢包重传请求
RTCP PLI/FIR:关键帧请求

WebRTC 配置建议

视频通话场景
FEC 冗余度:15-25%
NACK 启用:是
RTX 启用:是
保护模式:ULPFEC 或 FlexFEC 1D

直播推流场景
FEC 冗余度:20-30%
NACK 启用:根据延迟要求(低延迟启用,高延迟禁用)
保护模式:FlexFEC 2D(适合突发丢包)

点播场景
FEC 冗余度:10-15%
NACK 启用:是(延迟要求不严格)
保护模式:ULPFEC

总结

WebRTC 的 ARQ+FEC 混合方案通过以下机制实现了高效的丢包恢复:

  1. 多层次的 ARQ:NACK + RTX,实现快速重传
  2. 灵活的 FEC:ULPFEC + FlexFEC,支持多种保护模式
  3. 自适应策略:根据网络状况动态调整 ARQ 和 FEC 的比例
  4. 优先级控制:关键数据优先保护,保证用户体验
  5. 完善的反馈机制:通过 RTCP 实现网络状况的实时反馈

这使得 WebRTC 能够在各种网络条件下提供稳定、低延迟的实时通信体验。

留下评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注

Index