RTP header格式组成,见下图:
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 |
单一NAL单元模式:2 1 96 htons(g_rtspClients[is].seqnum++)
分片封包模式[0]: 2 0 96 htons(g_rtspClients[is].seqnum++)
分片封包模式[n]: 2 1 96 htons(g_rtspClients[is].seqnum++)
分片封包模式[N]: 2 0 96 htons(g_rtspClients[is].seqnum++)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp |
htonl(g_rtspClients[is].tsvid)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| synchronization source (SSRC) identifier |
10
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| contributing source (CSRC) identifiers |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
注:没有写的表示等于0;
分片封包模式[0]:第一个包;
分片封包模式[n]:中间的包;
分片封包模式[N]:最后一个包;
各个字段代表含义如下:
V:版本号,一般为2;
P:填充字段标识;
X:扩展头标识;
CC:CSRC计数,4比特
M:标志 1bit,在传输h264时表示h264 nalu的最后一包
PT:负载类型 7 bits, H264类型为96,荷载类型的赋值或者通过profile或者通过动态方式
SN:序列号16 bits
Timestamp:时间戳32bits,如果为视频的话,应该设置为1/9000,音频为1/8000,如果NAL单元没有
他自己的时间属性(即,parameter set and SEI NAL units),RTP时戳设置成访问单元主
编码图像的RTP时戳。
SSRC:32bits,用以识别同步源。
CSRC列表:0到15项,每项32比特,CSRC列表识别在此包中负载的所有贡献源。识别符的数目在CC域中
给定。若有贡献源多于15个,仅识别15个。CSRC识别符由混合器插入,并列出所有贡献源的
SSRC识别符。例如语音包,混合产生新包的所有源的SSRC标识符都被列出,这样可以在接收
端正确指示参与者。
contributing source (CSRC) identifiers 的组成:如下:
[StartCode] [NALU Header] [NALU Payload]
关于NALU使用RTP包进行发送可能的类型有:
1. 单一 NAL 单元模式(NALU的长度小于 MTU 大小的包)
即一个 RTP 包仅由一个完整的 NALU 组成。这种情况下 RTP NAL 头类型字段和原始的H.264的NALU
头类型字段是一样的。
对于 NALU的长度小于 MTU 大小的包,一般采用单一 NAL 单元模式。对于一个原始的H.264 NALU 单元常
由 [StartCode] [NALU Header] [NALU Payload] 三部分组成,其中 Start Code 用于标示这是一个NALU
单元的开始,必须是"00 00 00 01" 或"00 00 01",
NALU 头仅一个字节,其后都是 NALU 单元内容。打包时去除 "00 00 01" 或"00 00 00 01" 的开始码,把
其他数据封包的 RTP 包即可,有如下例子:
[00 0000 01 67 42 A0 1E 23 56 0E 2F ... ]
封装成 RTP 包将如下:
[ RTPHeader ] [ 67 42 A0 1E 23 56 0E 2F ]
(在这里要说明的是,如果客户端是通用的播放器,比如VLC或者JM的话需要将前导码去掉,但是如果使用的是
ffmpeg在客户端解码的话,发送前不需要去掉前导码,去掉之后可能会导致ffmpeg解码错误)。
NALU Header:
0
0 1 2 3 4 5 6 7 |
+-+-+-+-+-+-+-+-+|
|F|NRI| TYPE |
|0| 0 | 0 |
+-+-+-+-+-+-+-+-+|
H.264Payload 格式定义了三种不同的基本的负载(Payload)结构,接收端可能通过RTP Payload的第一个
字节来识别它们。这一个字节类似NALU 头的格式,而这个头结构的NAL 单元类型字段则指出了代表的是
哪一种结构,这个字节的结构如下:
F 1比特
NRI 2比特
Type 5比特
可以看出它和H.264 的NALU 头结构是一样的。
字段Type:这个RTP payload 中 NAL 单元的类型。 这个字段和 H.264 中类型字段的区别是,当type的
值为24-31表示这是一个特别格式的 NAL 单元,而H.264中,只取1-23是有效的值。
2 3. 分片封包模式(NALU的长度大于 MTU 大小的包)
用于把一个 NALU单元封装成多个 RTP 包。存在两种类型 FU-A 和 FU-B。类型值分别是 28 和 29。
而当 NALU 的长度超过 MTU 时,就必须对 NALU 单元进行分片封包。 也称为Fragmentation Units(FUs)。
将NALU拆分成小于MTU的数据包进行发送,如果使用的是VLC等网络播放器的话,需要设置FU header,如下图所示:
如果使用的是ffmpeg自行进行数据包接收与解码,则完全不必写FU header。
其实在后面的实际操作中会发现,SPS、PPS都是非常小,不到一百个字节,都是单个的NAl进行打包发送,而I帧一般都比较大,会采用分包发送,一般也是FU-A方式分片,其中MTU一般是1500个字节。FFmpeg中都有现成的源程序可以参考的。
对于H264的I帧、P帧等主要是FU(分片)发送,那么FU到底是怎样一个过程呢。
相同NAL单元的分片必须使用递增的RTP序号连续顺序发送(第一和最后分片之间没有其他的RTP包)。相似, NAL单元必须按照RTP顺序号的顺序装配。FUs不可以嵌套。即 一个FU 不可以包含另一个FU。运送FU的RTP时戳被设置成分片NALU的NALU的时刻。
FU-A的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
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| FU indicator | FU header | |
+--+--+--+--+--+--+--+--+--+--+--+-+--+--+--+--+ |
| |
| FU payload |
| |
| +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| :...OPTIONAL RTP padding |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
FU indicator : 1字节的分片单元指示
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+
|F|NRI| Type |
分片封包模式[0]: |0| 0 | 28 |
分片封包模式[n]: |0| 0 | 28 |
分片封包模式[N]: |0| 0 | 28 |
+---------------+
NRI: 2 bits, 00值指示NAL单元的内容不用于重建影响图像的帧间图像预测.这样的NAL单元可以被丢弃而不用冒影响图像完整性的风险。大于00的值指示NAL单元的解码要求维护引用图像的完整性。
注意:任何非零的NRI在H.264 解码器的处理是相同的。因此,接收者在传送NAL单元给解码器时不必操作NRI的值。NRI值必须根据分片NAL单元的NRI值设置。H.264编码器必须根据H.264规范设置NRI值。
当nal_unit_type 范围的是1到12。特别是,H.264规范要求对于nal_unit_type为6,9,10,11,12的NAL单元的NRI的值应该为0。
对于nal_unit_type等于7,8 (指示顺序参数集或图像参数集)的NAL单元,H.264编码器应该设置NRI为11 (二进制格式)。
对于nal_unit_type等于5的主编码图像的编码片NAL单元(指示编码片属于一个IDR图像), H.264编码器应设置NRI为11。
FU header: 1字节的分片单元头
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+
|S|E|R| Type |
分片封包模式[0]: |1|0|0| 0 |
分片封包模式[n]: |0|0|0| 0 |
分片封包模式[N]: |0|1|0| 0 |
+---------------+
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单元荷载类型定义。
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 |
单一NAL单元模式:2 1 96 htons(g_rtspClients[is].seqnum++)
分片封包模式[0]: 2 0 96 htons(g_rtspClients[is].seqnum++)
分片封包模式[n]: 2 1 96 htons(g_rtspClients[is].seqnum++)
分片封包模式[N]: 2 0 96 htons(g_rtspClients[is].seqnum++)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp |
htonl(g_rtspClients[is].tsvid)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| synchronization source (SSRC) identifier |
10
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| contributing source (CSRC) identifiers |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
注:没有写的表示等于0;
分片封包模式[0]:第一个包;
分片封包模式[n]:中间的包;
分片封包模式[N]:最后一个包;
各个字段代表含义如下:
V:版本号,一般为2;
P:填充字段标识;
X:扩展头标识;
CC:CSRC计数,4比特
M:标志 1bit,在传输h264时表示h264 nalu的最后一包
PT:负载类型 7 bits, H264类型为96,荷载类型的赋值或者通过profile或者通过动态方式
SN:序列号16 bits
Timestamp:时间戳32bits,如果为视频的话,应该设置为1/9000,音频为1/8000,如果NAL单元没有
他自己的时间属性(即,parameter set and SEI NAL units),RTP时戳设置成访问单元主
编码图像的RTP时戳。
SSRC:32bits,用以识别同步源。
CSRC列表:0到15项,每项32比特,CSRC列表识别在此包中负载的所有贡献源。识别符的数目在CC域中
给定。若有贡献源多于15个,仅识别15个。CSRC识别符由混合器插入,并列出所有贡献源的
SSRC识别符。例如语音包,混合产生新包的所有源的SSRC标识符都被列出,这样可以在接收
端正确指示参与者。
contributing source (CSRC) identifiers 的组成:如下:
[StartCode] [NALU Header] [NALU Payload]
关于NALU使用RTP包进行发送可能的类型有:
1. 单一 NAL 单元模式(NALU的长度小于 MTU 大小的包)
即一个 RTP 包仅由一个完整的 NALU 组成。这种情况下 RTP NAL 头类型字段和原始的H.264的NALU
头类型字段是一样的。
对于 NALU的长度小于 MTU 大小的包,一般采用单一 NAL 单元模式。对于一个原始的H.264 NALU 单元常
由 [StartCode] [NALU Header] [NALU Payload] 三部分组成,其中 Start Code 用于标示这是一个NALU
单元的开始,必须是"00 00 00 01" 或"00 00 01",
NALU 头仅一个字节,其后都是 NALU 单元内容。打包时去除 "00 00 01" 或"00 00 00 01" 的开始码,把
其他数据封包的 RTP 包即可,有如下例子:
[00 0000 01 67 42 A0 1E 23 56 0E 2F ... ]
封装成 RTP 包将如下:
[ RTPHeader ] [ 67 42 A0 1E 23 56 0E 2F ]
(在这里要说明的是,如果客户端是通用的播放器,比如VLC或者JM的话需要将前导码去掉,但是如果使用的是
ffmpeg在客户端解码的话,发送前不需要去掉前导码,去掉之后可能会导致ffmpeg解码错误)。
NALU Header:
0
0 1 2 3 4 5 6 7 |
+-+-+-+-+-+-+-+-+|
|F|NRI| TYPE |
|0| 0 | 0 |
+-+-+-+-+-+-+-+-+|
H.264Payload 格式定义了三种不同的基本的负载(Payload)结构,接收端可能通过RTP Payload的第一个
字节来识别它们。这一个字节类似NALU 头的格式,而这个头结构的NAL 单元类型字段则指出了代表的是
哪一种结构,这个字节的结构如下:
F 1比特
NRI 2比特
Type 5比特
可以看出它和H.264 的NALU 头结构是一样的。
字段Type:这个RTP payload 中 NAL 单元的类型。 这个字段和 H.264 中类型字段的区别是,当type的
值为24-31表示这是一个特别格式的 NAL 单元,而H.264中,只取1-23是有效的值。
2 3. 分片封包模式(NALU的长度大于 MTU 大小的包)
用于把一个 NALU单元封装成多个 RTP 包。存在两种类型 FU-A 和 FU-B。类型值分别是 28 和 29。
而当 NALU 的长度超过 MTU 时,就必须对 NALU 单元进行分片封包。 也称为Fragmentation Units(FUs)。
将NALU拆分成小于MTU的数据包进行发送,如果使用的是VLC等网络播放器的话,需要设置FU header,如下图所示:
如果使用的是ffmpeg自行进行数据包接收与解码,则完全不必写FU header。
其实在后面的实际操作中会发现,SPS、PPS都是非常小,不到一百个字节,都是单个的NAl进行打包发送,而I帧一般都比较大,会采用分包发送,一般也是FU-A方式分片,其中MTU一般是1500个字节。FFmpeg中都有现成的源程序可以参考的。
对于H264的I帧、P帧等主要是FU(分片)发送,那么FU到底是怎样一个过程呢。
相同NAL单元的分片必须使用递增的RTP序号连续顺序发送(第一和最后分片之间没有其他的RTP包)。相似, NAL单元必须按照RTP顺序号的顺序装配。FUs不可以嵌套。即 一个FU 不可以包含另一个FU。运送FU的RTP时戳被设置成分片NALU的NALU的时刻。
FU-A的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
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| FU indicator | FU header | |
+--+--+--+--+--+--+--+--+--+--+--+-+--+--+--+--+ |
| |
| FU payload |
| |
| +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| :...OPTIONAL RTP padding |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
FU indicator : 1字节的分片单元指示
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+
|F|NRI| Type |
分片封包模式[0]: |0| 0 | 28 |
分片封包模式[n]: |0| 0 | 28 |
分片封包模式[N]: |0| 0 | 28 |
+---------------+
NRI: 2 bits, 00值指示NAL单元的内容不用于重建影响图像的帧间图像预测.这样的NAL单元可以被丢弃而不用冒影响图像完整性的风险。大于00的值指示NAL单元的解码要求维护引用图像的完整性。
注意:任何非零的NRI在H.264 解码器的处理是相同的。因此,接收者在传送NAL单元给解码器时不必操作NRI的值。NRI值必须根据分片NAL单元的NRI值设置。H.264编码器必须根据H.264规范设置NRI值。
当nal_unit_type 范围的是1到12。特别是,H.264规范要求对于nal_unit_type为6,9,10,11,12的NAL单元的NRI的值应该为0。
对于nal_unit_type等于7,8 (指示顺序参数集或图像参数集)的NAL单元,H.264编码器应该设置NRI为11 (二进制格式)。
对于nal_unit_type等于5的主编码图像的编码片NAL单元(指示编码片属于一个IDR图像), H.264编码器应设置NRI为11。
FU header: 1字节的分片单元头
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+
|S|E|R| Type |
分片封包模式[0]: |1|0|0| 0 |
分片封包模式[n]: |0|0|0| 0 |
分片封包模式[N]: |0|1|0| 0 |
+---------------+
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单元荷载类型定义。
FU payload : 分片单元荷载。
下面再进一步解释一下时间戳增量是怎么计算出来的:
对于PAL制式的视频而言,每秒摄像头会采集 25 帧 数据,那么,每采集到 1帧 耗时 1/25 s ,如果我们设计为1个RTP包只包含1帧数据,并且一次发送1帧,那么,要想网络流量均匀,则时间戳增量应该设计为 1/25 s . 而在一般的RTP协议的实现中,时间戳单位不是 秒(s),而约定为采样频率的倒数,由于一般视频的采样频率是 90000,故时间戳单位为 1/90000 s,因此,实际的时间戳增量 = 时间戳增量 ( 1/25 s ) / 时间戳单位(1/90000 s) = 3600