1. RTP数据包格式
RTP报文头格式(见RFC3550 Page12):
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 12 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X| CC |M| PT | sequence number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| synchronization source (SSRC) identifier
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| contributing source (CSRC) identifiers |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
V:RTP协议的版本号,占2位,当前协议版本号为2
-
P:填充标志,占1位,如果P=1,则在该报文的尾部填充一个或多个额外的八位组,它们不是有效载荷的一部分。
-
X:扩展标志,占1位,如果X=1,则在RTP报头后跟有一个扩展报头
-
CC:CSRC计数器,占4位,指示CSRC 标识符的个数
-
M: 标记,占1位,不同的有效载荷有不同的含义,对于视频,标记一帧的结束;对于音频,标记会话的开始。
-
PT: 有效荷载类型,占7位,用于说明RTP报文中有效载荷的类型,如GSM音频、JPEM图像等,在流媒体中大部分是用来区分音频流和视频流的,这样便于客户端进行解析。
-
序列号:占16位,用于标识发送者所发送的RTP报文的序列号,每发送一个报文,序列号增1。这个字段当下层的承载协议用UDP的时候,网络状况不好的时候可以用来检查丢包。同时出现网络抖动的情况可以用来对数据进行重新排序,序列号的初始值是随机的,同时音频包和视频包的sequence是分别记数的。
-
时戳(Timestamp):占32位,必须使用90 kHz 时钟频率。时戳反映了该RTP报文的第一个八位组的采样时刻。接收者使用时戳来计算延迟和延迟抖动,并进行同步控制。
-
同步信源(SSRC)标识符:占32位,用于标识同步信源。该标识符是随机选择的,参加同一视频会议的两个同步信源不能有相同的SSRC。
-
特约信源(CSRC)标识符:每个CSRC标识符占32位,可以有0~15个。每个CSRC标识了包含在该RTP报文有效载荷中的所有特约信源。
取一段码流如下:
80 e0 00 1e 00 00 d2 f0 00 00 00 00 41 9b 6b 49 €?....??....A?kI
e1 0f 26 53 02 1a ff06 59 97 1d d2 2e 8c 50 01 ?.&S....Y?.?.?P.
cc 13 ec 52 77 4e e50e 7b fd 16 11 66 27 7c b4 ?.?RwN?.{?..f'|?
f6 e1 29 d5 d6 a4 ef3e 12 d8 fd 6c 97 51 e7 e9 ??)????>.??l?Q??
cfc7 5e c8 a9 51 f6 82 65 d6 48 5a 86 b0 e0 8c ??^??Q??e?HZ????
其中,
80 是V_P_X_CC
e0 是M_PT
00 1e 是SequenceNum
00 00 d2 f0 是Timestamp
00 00 00 00是SSRC
把前两字节换成二进制如下
1000 0000 1110 0000
按顺序解释如下:
10 是V;
0 是P;
0 是X;
0000 是CC;
1 是M;
110 0000 是PT;
2. 网络抽象层单元 (NALU)
NALU 头由一个字节组成, 它的语法如下:
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
F: 1 个比特. forbidden_zero_bit. 在H.264 规范中规定了这一位必须为 0.
NRI: 2 个比特.nal_ref_idc. 取 00 ~ 11, 似乎指示这个 NALU 的重要性, 如 00 的 NALU 解码器可以丢弃它而不影响图像的回放. 不过一般情况下不太关心这个属性.
Type: 5 个比特.nal_unit_type. 这个 NALU 单元的类型.
Type Packet Type name
0 没有定义
1 不分区,非IDR图像的片
2 片分区A
3 片分区B
4 片分区C
5 IDR图像中的片
6 补充增强信息单元(SEI)
7 SPS
8 PPS
9 序列结束
10 序列结束
11 码流借宿
12 填充
13-23 保留
1-23 NAL unit Single NAL unit packetper H.264
24 STAP-A 单一时间的组合包
25 STAP-B 单一时间的组合包
26 MTAP16 多个时间的组合包
27 MTAP24 多个时间的组合包
28 FU-A 分片的单元
29 FU-B 分片的单元
30-31 没有定义
H264 over RTP基本上分三种类型:
接收者可以通过RTP荷载的第一个字节后5位(Type)识别荷载结构:
-
单个NAL单元包:荷载中只包含一个NAL单元。NAL头类型域等于原始 NAL单元(NALU)类型,即Type在范围1到23之间。
-
聚合包(组合包):本类型用于聚合多个NAL单元到单个RTP荷载中。本包有四种版本,单时间聚合包类型A (STAP-A),单时间聚合包类型B (STAP-B),多时间聚合包类型(MTAP)16位位移(MTAP16), 多时间聚合包类型(MTAP)24位位移(MTAP24)。赋予STAP-A, STAP-B, MTAP16, MTAP24的NAL单元类型号(Type)分别是 24,25, 26, 27
-
分片包:用于分片单个NAL单元到多个RTP包。现存两个版本FU-A,FU-B,用NAL单元类型 (Type)28,29标识。
常用的打包时的分包规则是:如果小于MTU采用单个NAL单元包,如果大于MTU就采用FUs分片方式。
3.分包规则
3.1单个NAL单元包(1-23)
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| RTP Header |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|F|NRI| type | |
+-+-+-+-+-+-+-+-+ |
| |
| Bytes 2..n of a Single NAL unit |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
图3
对于 NALU(NAL单元)的长度小于 MTU 大小的包, 一般采用单一 NAL 单元模式.
定义在此的NAL单元包必须只包含一个。RTP序号必须符合NAL单元的解码顺序。这种情况下,NAL单元的第一字节和RTP荷载头第一个字节重合。如上图。
对于一个原始的 H.264 NALU 单元常由 [Start Code] [NALU Header] [NALU Payload] 三部分组成, 其中 Start Code 用于标示这是一个 NALU 单元的开始, 必须是 “00 00 00 01” 或 “00 00 01”, NALU 头仅一个字节, 其后都是 NALU 单元载荷。
打包时去除 “00 00 01” 或 “00 00 00 01” 的开始码, 把其他数据封装成 RTP 包即可。
如有一个 H.264 的 NALU 是这样的:
[00 00 00 01 67 42 A0 1E 23 56 0E 2F … ]
这是一个序列参数集 NAL 单元。 [00 00 00 01] 是四个字节的开始码, 67 是 NALU 头, 42 开始的数据是 NALU 载荷.
封装成 RTP 包将如下:
[ RTP Header ] [ 67 42 A0 1E 23 56 0E 2F … ]
即只要去掉 4 个字节的开始码就可以了.
3.2组合封包模式(24-27)
当 NALU 的长度特别小时, 可以把几个 NALU 单元封在一个 RTP 包中.
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| RTP Header |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|STAP-A NAL HDR | NALU 1 Size | NALU 1 HDR |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NALU 1 Data |
: :
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | NALU 2 Size | NALU 2 HDR |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NALU 2 Data |
: :
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
这种模式下,有多个NALU载荷,多个NALU头。
3.3分片单元(FU-A)(28-29)
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| FU indicator | FU header | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| |
| FU payload |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
FU-A RTP 载荷格式
其中 FU indicator 8位格式为:
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
FU header 格式:
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|S|E|R| Type |
+---------------+
图4
当 NALU 的长度超过 MTU 时, 就必须对 NALU 单元进行分片封包.
图 4 表示FU-A的RTP荷载格式。FU-A由1字节的分片单元指示(FU indicator)、1字节的分片单元头(FU header)和分片单元荷载组成。
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 Header中的Type。
打包时,原始的NAL头的前三位为FU indicator的前三位,原始的NAL头的后五位(Type)为FU header的后五位(Type)。
取一段码流分析如下:
80 60 01 0f 00 0e 10 00 00 00 00 00 7c 85 88 82€`..........|???
00 0a 7f ca 94 05 3b7f 3e 7f fe 14 2b 27 26 f8 ...??.;.>.?.+'&?
89 88 dd 85 62 e1 6dfc 33 01 38 1a 10 35 f2 14 ????b?m?3.8..5?.
84 6e 21 24 8f 72 62f0 51 7e 10 5f 0d 42 71 12 ?n!$?rb?Q~._.Bq.
17 65 62 a1 f1 44 dc df 4b 4a 38 aa 96 b7 dd 24 .eb??D??KJ8????$
前12字节是RTP Header
7c是FU indicator
85是FU Header
FU indicator(0x7C)和FU Header(0x85)换成二进制如下
0111 1100 1000 0101
按顺序解析如下:
0 是F
11 是NRI
11100 是FU Type,这里是28,即FU-A
1 是S,Start,说明是分片的第一包
0 是E,End,如果是分片的最后一包,设置为1,这里不是
0 是R,Remain,保留位,总是0
00101 是NAL Type,这里是5,说明是关键帧
打包时,FU indicator的F、NRI是NAL Header中的F、NRI,Type是28(FU-A);FU Header的S、E、R分别按照分片起始位置设置,Type是NAL Header中的Type。
解包时,取FU indicator的前三位和FU Header的后五位,即0110 0101(0x65)为NAL类型。