RTP和RTCP协议原理

3 阅读10分钟

RTP & RTCP 协议精讲

一、RTP/RTCP 与 RTSP 的关系

上一篇讲的 RTSP 是"遥控器"——控制播/停/跳。它不搬运数据。搬运数据的是 RTP,监控传输质量的是 RTCP

RTSP:遥控器(控制层)  → "从第 30 秒开始播"
RTP :搬运工(数据层)  → 把编码后的帧打包发出去
RTCP:质检员(反馈层)  → "你丢了 3% 的包,抖动 12ms"
RTSPRTPRTCP
功能控制"播/停/跳"搬运媒体数据反馈传输质量
传输层TCPUDP(或 TCP 交错)UDP(RTP 端口 +1)

二、RTP 包格式

RTP 头部只有 12 字节固定头 + 载荷。极度精简,尽可能多的字节留给实际音视频数据。

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X|  CC   |M|     PT      |       sequence number         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           timestamp                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                             SSRC                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                          载荷 (Payload)                       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

逐字段:

字段位数含义
V2 bit版本,固定为 2
P1 bit填充标志
X1 bit是否有头部扩展
CC4 bitCSRC 数量(混音场景)
M1 bit标记位。视频中通常标记一帧最后一个分片
PT7 bit载荷类型(96=动态分配的 H.264 等)
Sequence Number16 bit每包递增 1,用于丢包检测和重排序
Timestamp32 bit采样时刻,不是发送时刻(详解见下文)
SSRC32 bit同步源标识,随机生成,唯一标识一路流

三、RTP 三大核心机制(重点)

3.1 序列号——丢包检测

每发一个 RTP 包递增 1。接收方通过连续性检测丢包:

收到: 100, 101, 102, 105, 106
                ↑
         103, 104 丢了,gap = 2

16 位会从 65535 回绕到 0,用 int16_t 差值即可正确处理。

3.2 时间戳——播放定时与同步(最重要)

时间戳标记的不是"发送时刻",而是载荷中第一个采样的时刻。 这是最容易误解的点。

单位取决于时钟频率,由编码格式决定:

编码时钟频率每帧递增含义
G.711 音频8000 Hz160160/8000 = 20ms 一帧
AAC 音频44100 Hz10241024/44100 ≈ 23.2ms
H.264 视频90000 Hz36003600/90000 = 40ms(25fps)

为什么视频用 90kHz? 因为 90000 能被所有常见帧率整除:24fps→3750, 25fps→3600, 30fps→3000, 60fps→1500。

关键规则:同一视频帧的所有 RTP 分片,时间戳相同。

一帧 4500 字节拆成 3 个 RTP 包(MTU 1400):

RTP(seq=200, ts=180000, M=0)  ← 帧数据的第 1 段
RTP(seq=201, ts=180000, M=0)  ← 帧数据的第 2 段
RTP(seq=202, ts=180000, M=1)  ← 帧数据的第 3 段,M=1 标记帧结束

序列号递增,时间戳不变。接收方靠 M=1 或 FU-A 的 E 标志判断一帧组装完毕。

3.3 SSRC——标识唯一的媒体源

32 位随机标识符。一个摄像头的视频流一个 SSRC,音频流另一个 SSRC。

视频流: SSRC=0xAAAA, 所有视频包共享
音频流: SSRC=0xBBBB, 所有音频包共享

SSRC 可能因冲突而变,但 CNAME(在 RTCP SDES 中)永远不变,用来关联"换了 SSRC 但其实是同一个源"的流。


四、H.264 载荷打包(工程重点)

RTP 头部只解决了"从哪来、第几个、什么时刻"。编码数据如何塞进载荷,由载荷格式定义。H.264 最常见,有三种打包方式:

4.1 单 NAL 单元——小包直接放

SPS、PPS 这种小 NAL 直接放入载荷:

RTP Header (12 bytes) + NAL Unit (完整)

4.2 FU-A 分片——大包拆开

IDR 帧可能几十 KB,超过 MTU,需要拆分。FU-A(Fragmentation Unit type A)是最常用的打包方式。

RTP Header (12 bytes)
+ FU Indicator (1 byte): [F | NRI | Type=28]
+ FU Header    (1 byte): [S | E | R | Type]
+ FU Payload   (分片数据)

FU Header 中:
  S=1: 这是首个分片
  E=1: 这是最后分片
  Type: 原始 NAL 类型(如 5=IDR)

完整示例: 一个 4500 字节的 IDR NAL(Type=5),拆成 4 个 RTP 包:

包 1 (seq=200, ts=180000, M=0):
  FU Indicator: 0x7C  (F=0, NRI=3, Type=28)
  FU Header:    0x85  (S=1, E=0, Type=5)  ← 首片
  Payload: 第 1~1398 字节

包 2 (seq=201, ts=180000, M=0):
  FU Indicator: 0x7C
  FU Header:    0x05  (S=0, E=0, Type=5)  ← 中间
  Payload: 第 1399~2796 字节

包 3 (seq=202, ts=180000, M=0):
  FU Indicator: 0x7C
  FU Header:    0x05
  Payload: 第 2797~4194 字节

包 4 (seq=203, ts=180000, M=1):
  FU Indicator: 0x7C
  FU Header:    0x45  (S=0, E=1, Type=5)  ← 尾片,M=1 标记帧结束
  Payload: 第 4195~4500 字节

组装: SSRC相同 + 时间戳相同 + 序列号连续 + 从S=1到E=1
     → 去掉 FU-A 头部,恢复原始 NAL

4.3 STAP-A 聚合——多个小包合并

SPS+PPS 常聚合在一个 RTP 包中发送:

RTP Header (12 bytes)
+ STAP-A Header (1 byte, Type=24)
+ NAL1 Size (2 bytes) + NAL1 Data  ← 如 SPS
+ NAL2 Size (2 bytes) + NAL2 Data  ← 如 PPS

五、RTCP 协议(重点)

5.1 基础

RTCP 与 RTP 成对出现,使用 RTP 端口 +1:

RTP:  UDP 30000
RTCP: UDP 30001

五种包类型:

PT类型作用
200SR (Sender Report)发送统计 + NTP↔RTP 时间映射
201RR (Receiver Report)丢包、抖动、延迟统计
202SDES源描述(CNAME 等)
203BYE通知离开
204APP应用自定义

5.2 SR——音视频同步的关键

发送方周期性发送 SR,建立 NTP 时间 ↔ RTP 时间戳 的映射。这是实现音视频同步的核心。

SR 关键字段:

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|              NTP timestamp, most significant word             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|              NTP timestamp, least significant word            |  ← 8 字节 NTP 时间
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                         RTP timestamp                         |  ← 同一时刻的 RTP 时间戳
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                     sender's packet count                     |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                      sender's octet count                     |  ← 累计发送字节数
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

音视频同步过程:

摄像头同时发出视频 SR 和音频 SR:

视频 SR (SSRC=0xAAAA):
  NTP = T₀ (如 2024-01-15 12:00:00.000)
  RTP_TS = 3450012 (90kHz)

音频 SR (SSRC=0xBBBB):
  NTP = T₀ (同一时刻)
  RTP_TS = 98765 (8kHz)

接收方建立映射:
  视频: T₀ ↔ 3450012
  音频: T₀ ↔ 98765

后续收到视频包(ts=3453612):
  时间 = T₀ + (3453612 - 3450012)/90000 = T₀ + 40ms

后续收到音频包(ts=99085):
  时间 = T₀ + (99085 - 98765)/8000 = T₀ + 40ms

两者相同 → 同步播放 ✓

5.3 RR——传输质量反馈

接收方向发送方反馈,核心字段:

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| fraction lost |       cumulative number of packets lost       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           extended highest sequence number received           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                      interarrival jitter                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                         last SR (LSR)                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                   delay since last SR (DLSR)                  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

四个核心指标:

1. Fraction Lost(8 位)—— 本轮丢包率

fraction_lost = (期望包数 - 实际收到) / 期望包数 × 256

示例: 期望 100 包,收到 97 → fraction = 3/100×2568 → 丢包率约 3.1%

2. Cumulative Lost(24 位)—— 累计丢包总数

cumulative_lost = (最高序列号 - 初始序列号 + 1) - 实际收到包数

3. Interarrival Jitter(32 位)—— 网络抖动

RFC 3550 给出的指数加权移动平均算法:

int transit = arrival_time - rtp_timestamp;
int d = transit - last_transit;       // 相邻两包的 transit 差值
last_transit = transit;
jitter = jitter + (abs(d) - jitter) / 16;  // EWMA,除以 16 是定点运算

单位是 RTP 时钟 tick。视频 90kHz 下,jitter=4500 表示 50ms 抖动。

4. LSR + DLSR——计算往返延迟(RTT)

T₁: 发送方发出 SR (NTP=T₁)
T₂: 接收方收到 SR → LSR = T₁ 的中间 32 位
T₃: 接收方发出 RR → DLSR = T₃ - T₂
T₄: 发送方收到 RR

RTT = T₄ - LSR - DLSR

  T₁        T₂     T₃              T₄
  SR发出 → SR收到 → RR发出    →   RR收到
  ◄─ 网络 ─►       ◄── 网络 ──►
       ◄── DLSR ──►
  ◄──────── RTT = T₄ - LSR - DLSR ────────►

六、丢包恢复:NACK 与 FEC

RTP 本身"发了就忘",不提供重传。丢包恢复是上层的事。

NACK——请求重传

接收方告诉发送方"我没收到 seq=201",请求重传。代价是至少一个 RTT 的额外延迟。

接收方 → 发送方: RTCP NACK (PID=201)  "请重传 seq 201"
发送方 → 接收方: RTP (seq=201) [重传]

FEC——前向纠错

发送方额外发送纠错包,接收方直接恢复,不需要请求重传。

XOR FEC (最简单):
  原始包: P1, P2, P3
  FEC 包: F = P1 ⊕ P2 ⊕ P3

  如果 P2 丢失 → P2 = F ⊕ P1 ⊕ P3(即时恢复,0 额外延迟)
NACKFEC
原理请求重传冗余编码
额外带宽仅丢失时消耗固定开销(5%-20%)
恢复延迟≥ 1 RTT0(立即恢复)
适用场景延迟容忍、丢包率低延迟敏感、视频会议

七、Jitter Buffer(抖动缓冲区)

网络到达间隔不均匀,但播放必须均匀。Jitter Buffer 吸收抖动,平滑输出。

到达(不均匀):   35ms, 48ms, 22ms, 55ms
                    ↓  Jitter Buffer  ↓
播放(均匀):     40ms, 40ms, 40ms, 40ms

自适应策略:目标延迟 ≈ 2 × 测量抖动值,限制在 [min_delay, max_delay] 范围内。

  • 设太小 → 抖动没吸收完,播放卡顿
  • 设太大 → 流畅但延迟高

典型值:实时会议 60-200ms,安防监控 200-500ms。


八、NAT 穿越与安全

NAT 问题

RTP 走 UDP,与 RTSP 的 TCP 控制通道分离。NAT 设备只看到 TCP 连接,不知道 UDP 端口映射。

最常用的工程方案:RTP over TCP 交错模式(RTSP interleaved),牺牲延迟换可用性。所有 RTP/RTCP 复用 RTSP 的 TCP 连接,封装格式:

'$' (0x24) | Channel (1B) | Length (2B, 大端) | RTP/RTCP 数据

Channel: 0=视频RTP, 1=视频RTCP, 2=音频RTP, 3=音频RTCP。

SRTP 加密

标准 RTP 明文传输。SRTP(RFC 3711)增加加密+认证:

┌──────────┬──────────────────┬──────────────┐
│ RTP 头部  │  AES-128 加密载荷 │ HMAC-SHA1 认证│
│ (明文)    │                  │  Tag (10B)   │
└──────────┴──────────────────┴──────────────┘

WebRTC 使用 DTLS-SRTP 协商密钥。


九、关键数值速查

指标
RTP 固定头部12 字节
RTP+UDP+IP 总开销40 字节/包
MTU1500 字节
RTP 有效载荷上限~1400 字节
视频时钟90 kHz
RTCP 带宽上限会话总带宽的 5%
RTCP 最小发送间隔5 秒
H.264 每帧 RTP 包数ceil(帧大小 / 1400)

RTP 包大小实例(H.264, 25fps, 2Mbps):

每帧平均 = 2,000,000 / 8 / 25 = 10,000 字节
每帧 RTP 包数 = ceil(10000/1400) ≈ 8 包
每秒包数 = 200 pps
头部开销 = 200 × 12 = 2,400 B/s = 19.2 kbps(仅占码率 0.96%)

总结

RTSP → 控制"播什么"
RTP  → 负责"怎么搬"  → 12字节头 + 序号 + 时间戳 + SSRC
RTCP → 监控"搬得怎样" → SR(时间映射) + RR(丢包/抖动/RTT)

RTP 刻意轻量——不做重传、不做拥塞控制、不做流量管理。它只做一件事:把带序号和时间戳的小包发出去。复杂的事情全部留给上层(FEC/NACK/Jitter Buffer/拥塞控制)和搭档(RTCP 反馈)处理。

理解这套骨架,上层的自适应码率、丢包恢复、音视频同步就都有了立足点。