音视频学习(九十四):NACK和RTX

实时通信为何不能用 TCP?

很多人第一反应是:既然丢包,为什么不用 TCP?答案很简单------延迟不可接受

TCP 的重传机制是"可靠优先",而实时音视频是"时效优先"。一个已经过了播放时间窗口的数据包,即使成功重传,也失去了意义。因此,实时系统必须在两个目标之间取得平衡:

  • 尽量恢复丢失数据(保证质量)
  • 严格控制时延(保证实时性)

NACK 与 RTX 正是在这个约束下诞生的。

NACK:轻量级的丢包反馈机制

NACK(Negative Acknowledgement)的作用是:告诉发送端"我缺了哪些包"

在 RTP 传输中,每个数据包都有递增的序列号。当接收端发现序列号不连续时,就可以判断存在丢包。例如:

复制代码
接收序列:1001, 1002, 1004
缺失序列:1003

此时接收端不会立刻请求重传,而是进入一个短暂的"观察窗口"(通常 20~50ms),以避免将网络乱序误判为丢包。这是实际工程中非常关键的一步。

一旦确认丢失,接收端会通过 RTCP 发送 NACK 报文。该报文采用紧凑编码方式,用一个起始序号(PID)和一个位掩码(BLP)描述多个丢失包,从而减少控制信令的开销。

更重要的是,NACK 并不是"立即逐包发送",而是批量聚合,通常每 10~30ms 发送一次。这种策略可以显著降低 RTCP 带宽占用,同时避免反馈风暴。

RTX:独立通道的重传机制

如果说 NACK 是"请求",那么 RTX(Retransmission)才是"执行"。

RTX 的设计并不是简单地"再发一遍原包",而是引入了一套独立机制:

  • 使用独立 SSRC(区分原始流与重传流)
  • 使用独立 Payload Type
  • 在 Payload 中携带原始序列号(OSN)

这样设计的好处是:接收端可以明确识别这是重传数据,并进行正确恢复,而不会干扰正常的解码流程。

一个典型的 RTX 包结构如下:

复制代码
[ RTX Header ]
[ OSN(原始序列号) ]
[ 原始 RTP Payload ]

接收端在收到 RTX 包后,会提取 OSN,将其还原为原始 RTP 包,再交给抖动缓冲区(Jitter Buffer)进行重排序与播放。

完整链路:从丢包到恢复

将 NACK 与 RTX 串联起来,可以得到一个完整的数据恢复闭环:

  1. 接收端检测到序列号缺失
  2. 等待短暂窗口确认丢包
  3. 发送 RTCP NACK 请求
  4. 发送端在缓存中查找对应 RTP 包
  5. 封装为 RTX 包重新发送
  6. 接收端恢复原始包并插入播放队列

这套机制的关键不在"流程",而在每一步的时序控制。如果任何一个环节过慢,重传数据就可能赶不上播放时间,从而失效。

核心:发送缓存设计

NACK/RTX 能否生效,取决于发送端是否还能"找回历史数据"。因此,RTP 发送缓存(Packet History)是整个机制的核心组件

缓存设计需要权衡三个关键因素:

1. 缓存时长

通常设置为:

复制代码
缓存时间 ≈ RTT + 抖动裕量(一般 200ms ~ 1000ms)

过短:包已经被清理,无法重传

过长:内存占用过高,影响系统稳定性

2. 数据结构

常见实现:

复制代码
std::map<uint16_t, RtpPacket>

或者环形缓冲区(更高效)

3. 清理策略

  • 按时间淘汰(推荐)
  • 按容量淘汰(兜底)

什么时候"放弃重传"?

并不是所有丢包都值得重传。工程中必须明确"放弃条件":

1. 超过播放时延

如果数据已经错过播放时间窗口(例如 >100ms):

即使重传成功,也不会被使用

2. 重试次数限制

通常限制为 2~3 次:

避免无效重传浪费带宽

3. 关键帧丢失

如果丢的是视频关键帧(I帧):

直接发送 PLI(请求新关键帧)比 NACK 更有效

与其他机制的协同

NACK/RTX 并不是孤立存在,而是与多个机制协同工作:

1. 与 FEC(前向纠错)

  • FEC:无延迟,但增加带宽
  • NACK:低带宽,但有往返延迟

实际系统通常采用组合策略:

复制代码
轻微丢包 → FEC
严重丢包 → NACK + RTX

2. 与拥塞控制

重传流量会增加带宽压力,因此必须纳入带宽估计算法(如 GCC)。否则容易导致网络进一步拥塞,形成"重传风暴"。

3. 与抖动缓冲

RTX 恢复的数据必须正确插入时间序列,否则会导致解码错误甚至崩溃。因此 Jitter Buffer 必须支持:

  • 乱序插入
  • 时间戳对齐
  • 丢帧容忍

音频与视频的差异策略

一个常被忽略的事实是:音频和视频对重传的依赖完全不同

视频

  • 强依赖 NACK/RTX
  • 丢包会导致花屏或预测链断裂

音频

  • 通常不启用重传
  • 使用 PLC(Packet Loss Concealment)进行补偿

原因在于:音频对延迟极其敏感,而单个丢包影响相对较小。

常见问题与踩坑经验

  • 发了 NACK,但发送端缓存已被清理
  • RTX SSRC 映射错误,接收端无法识别
  • 重传包未正确恢复 OSN,导致解码异常
  • NACK 发送过于频繁,引发 RTCP 风暴
  • 未做乱序容忍,误判丢包

这些问题的本质都指向一点:时序与状态管理比协议本身更重要

总结:本质是"延迟与可靠性的博弈"

NACK 与 RTX 并不是简单的"补包机制",而是一套围绕实时性设计的动态系统。它的核心思想可以概括为一句话:

在有限时间窗口内,用最小代价恢复最有价值的数据

相关推荐
NGBQ121384 小时前
Adobe-Premiere-Pro-2026-26.0.2.2-m0nkrus 全解析:专业视频编辑软件深度指南
前端·adobe·音视频
芯跳加速4 小时前
AI 视频自动化学习日记 · 第三天
人工智能·学习·ai·自动化·音视频
chushiyunen4 小时前
python edge-tts实现tts文本转语音、音频
数据库·python·音视频
液态不合群6 小时前
OpenCV多线程编程:从单线程到多线程的视频处理
人工智能·opencv·音视频
飞Link6 小时前
具身智能音频处理核心框架 PyAudio 深度拆解与实战
开发语言·python·音视频
弓.长.7 小时前
ReactNative for OpenHarmony项目鸿蒙化三方库:react-native-video — 视频播放组件
react native·音视频·harmonyos
大傻^8 小时前
Spring AI Alibaba 多模态开发:集成视觉理解与视频分析能力
人工智能·spring·音视频·springai·springaialibaba·混合检索
却道天凉_好个秋9 小时前
音视频学习(九十三):CompoundRTCP
音视频·rtcp·compoundrtcp
山栀shanzhi9 小时前
【FFmpeg】音视频MP4封装格式转封装MOV
ffmpeg·音视频