流媒体协议

RTSP 在上层发号施令(管控制),RTP 在底层埋头搬砖(运数据),RTCP 在旁边盯着质量(做监督)
三者关系:
- RTSP:控制播放、暂停和会话参数;RTP/RTCP 还可通过 RTSP 的 TCP 连接交错传输,不能理解为控制连接一定不承载媒体字节。
- RTP:封装媒体载荷、时间戳和序列号,常运行于 UDP 之上,也可使用 TCP 等承载;RTP 本身不保证可靠、有序或按时送达。
- RTCP:发送传输质量、同步和参与者信息;NACK 等反馈属于扩展机制,是否重传或调整码率由协议协商和实现决定。
传统 RTP/UDP 会话通常使用偶数 RTP 端口及相邻的奇数 RTCP 端口,但这是常见约定。SDP 可以明确协商 RTCP 端口,rtcp-mux 还允许两者共用同一端口:
- RTP 占用偶数端口 $N$(如
9000)传输按载荷格式封装的媒体数据(通常为编码后的码流); - RTCP 占用相邻的奇数端口 $N+1$(如
9001)传输控制反馈报告。
RTP(实时传输协议)
- RTP/RTCP 是实际传输数据的协议
头部
0 1 2 3
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X| CC |M| PT | sequence number | // 4Byte 固定
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp | // 4Byte 固定
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| synchronization source (SSRC) identifier | // 4Byte 固定
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| contributing source (CSRC) identifiers | <- 每个4 字节, 共 CC 个
| ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| extension header (opt) | ext_length | <- 16 bit + 16 bit
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| extension data … | <- ext_length × 4 字节
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| payload … |
固定头部字段
固定头部最少 12 字节;CSRC 列表和扩展头会增加总头部长度:
- V (2 bits):版本号(通常为2)
- P (1 bit):填充位(末尾有填充字节时置1)
- X (1 bit):扩展头标志
- CC (4 bits):CSRC计数器(混合流数量)
- M (1 bit):含义由载荷规范定义;H.264 通常标记访问单元末尾,某些音频格式用于标记 talkspurt 开始,不能通用解释为“静音”
- PT (7 bits):载荷类型。RTP/AVP 中 PCMU(G.711 μ-law)静态 PT 为 0,PCMA(A-law)为 8;H.264 通常使用 SDP 协商的动态 PT,96 只是常见取值。
- Sequence Number:包序号(检测丢包和乱序)
- Timestamp:时间戳(基于采样频率的时钟值)
- SSRC:会话内同步源的 32 位标识,一个会话可以有多个 SSRC;发送方需检测并处理冲突
- CSRC:贡献源列表(混流时使用)
扩展头部
扩展头部是RTP协议的一个重要扩展机制,它是RTP头部的扩展标志(X)位决定的,它允许在RTP数据包中添加额外的信息,以满足特定应用的需求。扩展头部的格式如下:
- 扩展头部标识符:占16位,用于标识扩展头部的类型和格式。不同的标识符对应不同的扩展头部定义,使得接收端能够正确解析扩展头部的内容。
- 扩展头部长度:16 位,按 32 位字计数,不包含前面的 4 字节扩展头自身。
- 扩展头部内容:扩展头部的内容根据具体的扩展头部标识符而定,可以包含各种类型的信息,如时间戳扩展、序列号扩展、加密信息等。扩展头部的灵活性使得RTP协议能够适应不断发展的应用需求。扩展头部的使用提高了RTP协议的适应性和扩展性,使得RTP协议能够在多种复杂的网络环境中稳定运行,并满足不同应用的特定需求。
RTP会话过程
会话建立
RTP会话的建立是实时数据传输的起点,涉及多个关键步骤,确保数据能够正确传输到接收端。
- 确定传输地址:按会话协商结果确定 RTP/RTCP 的传输地址、端口及是否复用。
- 初始化 RTP 和 RTCP:设置源标识、序列号、时间戳及 RTCP 报告状态。
- SSRC 的生成:通常随机选取,会话内可能发生碰撞,必须按协议处理;不能保证随机生成就绝不重复。
载荷
H.264 封装
RTP 是传输协议,H.264 是编码格式。RTP 对 H.264 码流进行封装传输。
┌─────────────┬──────────────────────────────────────────┐
│ RTP Header │ RTP Payload │
│ (12字节) │ (H.264 NALU数据,不含起始码) │
└─────────────┴──────────────────────────────────────────┘
H.264 NALU 头格式
在RTP头之后就是数据负载部分,在H264的负载封装中,第一个字节包含了数据传输的类型
+---------------+
|F| NRI | Type |
+---------------+
1 2 5 bits
h264的NALU头长度为:1字节
| 字段 | 长度 | 说明 |
|---|---|---|
| F (forbidden_zero_bit) | 1 bit | 必须为 0 |
| NRI (nal_ref_idc) | 2 bit | NALU 重要性,值越大越重要(0-3) |
| Type | 5 bit | NALU 类型 |
Type 取值:
- 1-23:单个 NAL 单元包
- 24-27:组合包(STAP-A、STAP-B、MTAP16、MTAP24)
- 28-29:分片包(FU-A、FU-B)
例如:
0x67(二进制:0110 0111)- 拆解:F=
0,NRI=11(最高重要性),Type=7(SPS)。
- 拆解:F=
0x68(二进制:0110 1000)- 拆解:F=
0,NRI=11(最高重要性),Type=8(PPS)。
- 拆解:F=
0x65(二进制:0110 0101)- 拆解:F=
0,NRI=11(最高重要性),Type=5(IDR 图像的编码切片)。
- 拆解:F=
0x41(二进制:0100 0001)- 拆解:F=
0,NRI=10(中重要性),Type=1(非 IDR 图像的编码切片;I/P/B 类型需继续解析 slice header)。
- 拆解:F=
单 NALU 模式

单一NAL单元模式是最简单的RTP封装方式,每个RTP包只包含一个H.264的NAL单元。这种模式适用于NAL单元大小较小,且网络条件较好,不需要对NAL单元进行分片或聚合的场景。在这种模式下,RTP负载的第一个字节是NAL单元的头信息(1字节),紧接着是 NAL 单元的字节序列载荷(EBSP,可能含 emulation prevention 字节),不是直接去除防竞争字节后的 RBSP。封装和解封装过程简单,易于实现。由于每个RTP包只包含一个NAL单元,因此在解码端可以快速识别和处理每个NAL单元,减少了处理复杂度。适用于NAL单元较小(如SPS、PPS等参数集或较小的I帧切片)的场景,或者在网络带宽充足且对延迟要求不高的情况下。
- NAL 类型(NAL Unit Type): 1–23
- 结构: RTP 载荷直接就是一个完整的 NAL 单元(不含起始码 0x00000001),若要避免 IP 分片,需要从路径 MTU 中扣除 IP、UDP、RTP 及扩展等头部开销,再判断整个 NALU 是否能装入单包。
- 处理: 按 SSRC、序列号和 RTP 时间戳重排、检测丢包并组装访问单元;M 位可作为边界提示,但收到 M=1 并不能证明此前所有分片都已到齐。
分片封包(FU-A)
RTP Header + FU indicator (1字节) + FU header(1字节) + Fragment data
FU indicator :
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
Type = 28 表示这是FU-A
FU header :
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|S|E|R| Type |
+---------------+
- S(开始位): 1 bit, 当设置成1,指示分片NAL单元的开始。当跟随的FU荷载不是分片NAL单元荷载的开始,开始位设为0。
- E(结束位): 1 bit, 当设置成1,指示分片NAL单元的结束,即,荷载的最后字节也是分片NAL单元的最后一个字节。当跟随的 FU荷载不是分片NAL单元的最后分片,结束位设置为0。
- R(保留位): 1 bit, 保留位必须设置为0,接收者必须忽略该位。
- Type(类型):5 bit,原始NAL类型
H.265 封装
H.265 NALU头格式
+-------------------------------+
| F | Type | LayerId |TID|
+-------------------------------+
Bits: F(1), Type(6), LayerId(6), TID(3)
h265的NALU头长度为:2字节
| 字段 | 长度 | 说明 |
|---|---|---|
| F (forbidden_zero_bit) | 1 bit | 禁止位。最高位(第 15 位),必须为 0。若为 1 则标识该帧为无效帧、损坏帧。 |
| Type (nal_unit_type) | 6 bit | 帧类型(第 9 ~ 14 位)。0 - 31:VCL NAL 单元(携带编码视频图像数据的流,如 19/20 为 IDR 关键帧,1 为普通 P/B 帧)。32 - 63:非 VCL NAL 单元(控制数据流,如 32 为 VPS,33 为 SPS,34 为 PPS)。 |
| LayerId (nuh_layer_id) | 6 bit | 层标识(第 3 ~ 8 位)目前普通单视角的 IPC 编码中一般固定为 0 |
| TID (nuh_temporal_id_plus1) | 3 bit | 时间层 ID 加 1,合法值为 1~7,不能为 0;实际时间层 ID 为 TID − 1。单时间层码流常为 1,不能把所有码流都固定写成 1。 |