# FEC 前向纠错 · 不用往返就能恢复

> 重传要等一个往返，高延迟场景来不及。FEC 换个思路：提前发冗余包，收端丢了包能就地算回来，无需往返。这一章讲清 FEC 的异或恢复原理、ULPFEC 与 FlexFEC 两种实现、音频的 RED，以及 FEC 与重传「费带宽 vs 费往返」的权衡。

- 作者：David（道雾轩）
- 专栏：音视频与 WebRTC 详解（https://daiw.org/manual/webrtc.md）
- 最后更新：2026-07-14
- 原文：https://daiw.org/manual/webrtc/fec
- 转载与引用：请注明出处并附原文链接（https://daiw.org/about/copyright）

# FEC 前向纠错 · 不用往返就能恢复

[重传（NACK/RTX）](https://daiw.org/manual/webrtc/nack-rtx)有个软肋：要等一个 [RTT](https://daiw.org/manual/webrtc/connection-and-checks)。高延迟或急需低延迟时，重传来不及。**FEC（Forward Error Correction，前向纠错）** 换了个思路：**提前多发点冗余，让接收端丢了包能自己算回来，根本不用往返。** 这一章讲它。

## 思路：多发冗余，就地恢复

<Callout type="info">
  **FEC 的核心：发送端在媒体包之外，额外发一些「冗余包」；接收端一旦丢了个别媒体包，能用收到的媒体包 + 冗余包**就地算出**丢失的那个，无需请对端重传。** 最简单的形式是**异或（XOR）**：发送端把几个媒体包异或出一个冗余包一起发。接收端若丢了其中一个媒体包，用「其余媒体包 + 冗余包」再异或一次，就**恢复出**丢失的那个——就像「已知 A⊕B⊕C=P，丢了 B，用 A⊕C⊕P 就能把 B 算回来」。

  和[重传](https://daiw.org/manual/webrtc/nack-rtx)一比，FEC 的取舍很清楚：**它不需要往返**（丢包当下就能恢复，零延迟），但**恒定地多花带宽**（不管丢不丢，冗余包都得发）。所以：**低 RTT 用[重传](https://daiw.org/manual/webrtc/nack-rtx)更省**（丢了才重发），**高 RTT / 高丢包用 FEC 更值**（免往返，虽然费带宽）。WebRTC 常**两者结合**，按网络状况动态调冗余量。
</Callout>

## 两种实现：ULPFEC 与 FlexFEC

WebRTC 视频的 FEC 有两代实现（`modules/rtp_rtcp/source/` 及相关）：

| | ULPFEC | FlexFEC |
| --- | --- | --- |
| 全称 | Uneven Level Protection FEC | Flexible FEC |
| 冗余包放哪 | 常和媒体**捆在一起**（经 [RED](https://daiw.org/manual/webrtc/fec) 封装） | **独立的 [RTP 流](https://daiw.org/manual/webrtc/rtp-packet)**（自己的 SSRC） |
| 灵活性 | 保护模式较固定 | **灵活**：可跨包、按行 / 列保护，适配不同丢包模式 |
| 现状 | 较老 | 较新（[默认放低 PT 范围](https://daiw.org/manual/webrtc/payload-types)） |

**ULPFEC** 提供「不均等保护」——对一个包里更重要的部分给更强保护。**FlexFEC**（flexfec-03）更灵活，能配置成「保护连续 N 个包」或「按二维行列保护」，适应不同的[丢包分布](https://daiw.org/manual/webrtc/nack-rtx)（突发连丢 vs 零星散丢），且走[独立流](https://daiw.org/manual/webrtc/nack-rtx)不干扰媒体流。

## 音频的冗余：RED

音频对丢包的对抗，常用另一个更简单的机制——**RED（Redundant Encoding，冗余编码，RFC 2198）**：

<Callout type="info">
  **音频 RED 的做法：每个包里，除了当前这一小段音频，再**捎带上前几段音频的副本**。** 音频包很小、间隔很密（如每 20ms 一个），所以在一个包里塞进「当前 + 前一两段」代价不大。于是丢了一个包，它携带的那段音频，**很可能在下一个包里还带着一份副本**，直接就补上了——听感上几乎无损。这比给音频做复杂 FEC 简单得多，对语音这种「丢一小段就卡顿」的媒体特别有效。Opus 编解码器本身还内建了 **[in-band FEC](https://daiw.org/manual/webrtc/source-map)**（在下一个包里编码一份上一包的低码率版本），和 RED 异曲同工。音频靠这些「顺手捎带冗余」的手段，对丢包的韧性其实比视频还好。
</Callout>

## 谁决定发多少 FEC

FEC 的冗余量不是固定的——发太多浪费带宽、发太少防不住丢包。由 **`FecController`**（[可在工厂替换](https://daiw.org/manual/webrtc/factory-and-context)的 `fec_controller_factory`）根据当前的[丢包率](https://daiw.org/manual/webrtc/rtcp-reports)和[可用带宽](https://daiw.org/manual/webrtc/source-map)**动态决定**：网络好、丢包少，就少发甚至不发 FEC，把带宽全给媒体；丢包一上来，就加大冗余保护。这是又一个[实时系统「随网况自适应」](https://daiw.org/manual/webrtc/source-map)的体现——一切参数都在跟着网络实时调整。

## 小结与下一章

**FEC 前向纠错**用**冗余包**让接收端**就地恢复**丢包，**免往返**（对比[重传](https://daiw.org/manual/webrtc/nack-rtx)要一个 [RTT](https://daiw.org/manual/webrtc/connection-and-checks)），代价是**恒定多花带宽**——故低 RTT 靠重传、高 RTT / 高丢包靠 FEC，常结合。原理是**异或恢复**。视频有 **ULPFEC**（捆媒体）和 **FlexFEC**（独立流、灵活）两代；音频用 **RED**（包里捎带前几段副本）+ [Opus in-band FEC](https://daiw.org/manual/webrtc/source-map)。冗余量由 `FecController` 按[丢包](https://daiw.org/manual/webrtc/rtcp-reports)动态调。

重传和 FEC 都是「对抗丢包」。而更上游的问题是**该发多快**——发太快必堵、太慢浪费。这需要精确的**带宽反馈**，就是 **Transport-CC**。下一章讲这个连接第 4、第 5 部分的枢纽。👉 [Transport-CC 反馈](https://daiw.org/manual/webrtc/transport-cc)。

## 参考资料

- [1] libwebrtc 源码.`modules/rtp_rtcp/source/`（ulpfec / flexfec 相关）、`fec_controller` 接口。
- [2] IETF RFC 5109「ULPFEC」、RFC 8627「FlexFEC」、RFC 2198「RED」。
- [3] libwebrtc 源码.`g3doc/rtp.md`（flexfec-03 的 PT 分配）。
