1. 网络抽象层单元类型 (NALU)
NALU是H264用于网络传输的单元类型,一个完整的NALU单元一般是以0x000001或者0x00000001开始,其后跟的则是NALU头和NALU的数据;我们在网络传输的时候,会去掉开始的0x000001或者0x00000001的标志;一般需要将这些标志替换为RTP payload的头部(1个字节);
其中NALU数据就是RBSP数据;
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. 重要性指示,标志该NAL单元的重要性,值越大,越重要,解码器在解码处理不过来的时候,可以丢掉重要性为0的NALU,取 00 ~ 11。
nal_ref_idc. 0值和非零值的语义与H.264规范保持一致。换句话,00值指示NAL单元的内容不用于重建影响图像的帧间图像预测。这样的NAL单元可以被丢弃而不用冒影响图像完整性的风险。大于00的值指示NAL单元的解码要求维护引用图像的完整性。 除了上面指定的外, 根据本RTP荷载规范, 大于00的NRI值指示相对传输优先级, 象编码器决定的一样。 MANE可以使用本信息保护更重要的NAL单元。最高的传输优先级是11, 依次是 10, 01;00 最低。(注释: 任何非零的NRI在H.264 解码器的处理是相同的。因此,接收者在传送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; 对于映射其他的nal_unit_types到NRI值,下图可以使用,并且在某些环境有效,其它的映射也可以,依赖于应用以及使用的H.264/AVC Annex A profile。(注释: 在某些profile中数据分区不可用,即在Main或Baseline profiles. 因此, nal单元类型2, 3,4 只出现在视频流符合数据分区被允许的profile情况下,不会出现在符合MAIN/Baseline profile的流中)
Table 2. 编码片和主编码参考图像数据分区的编码片的NRI值的例子
NAL Unit Type Content of NAL unit NRI (binary) ---------------------------------------------------------------- 1 non-IDR coded slice 10 2 Coded slice data partition A 10 3 Coded slice data partition B 01 4 Coded slice data partition C 01
(注释: 非参考图像NRI值是00)
H.264编码器应该设置冗余编码参考图像的编码片和编码片分区NAL单元的NRI值为01 (二进制格式);对于NAL单元类型24~29的NRI的定义在下面第二个图给出;对于nal_unit_type范围在13到23的NAL单元的NRI值没有推荐的值,因为这些值保留给ITU-T,ISO/IEC. 对于nal_unit_type为0或30,31的NAL单元的NRI值也没有推荐的值,因为这些值的语义本文没有指定。
Type: 5 个比特. nal_unit_type. 这个 NALU 单元的类型. 简述如下:
0 没有定义 1-23 NAL单元 单个 NAL 单元包. 24 STAP-A 单一时间的组合包 25 STAP-B 单一时间的组合包 26 MTAP16 多个时间的组合包 27 MTAP24 多个时间的组合包 28 FU-A 分片的单元 29 FU-B 分片的单元 30-31 没有定义
(C表示重要性)
2. 打包模式
下面是 RFC 3550 中规定的 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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X| CC |M| PT | sequence number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| synchronization source (SSRC) identifier |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| contributing source (CSRC) identifiers |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
负载类型 Payload type (PT): 7 bits 序列号 Sequence number (SN): 16 bits 时间戳 Timestamp: 32 bits H.264 Payload 格式定义了三种不同的基本的负载(Payload)结构. 接收端可能通过 RTP Payload 的第一个字节来识别它们. 这一个字节类似 NALU 头的格式, 而这个头结构的 NAL 单元类型字段 则指出了代表的是哪一种结构,
这个字节的结构如下, 可以看出它和 H.264 的 NALU 头结构是一样的.
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
字段 Type: 这个 是RTP payload 中 NAL 单元的类型. 这个字段和 H.264 中类型字段的区别是, 当 type 的值为 24 ~ 31 表示这是一个特别格式的 NAL 单元, 而 H.264 中, 只取 1~23 是有效的值.
可能的结构类型分别有:
1. 单一 NAL 单元模式 即一个 RTP 包仅由一个完整的 NALU 组成. 这种情况下 RTP NAL 头类型字段和原始的 H.264的 NALU 头类型字段是一样的.
2. 组合封包模式 即可能是由多个 NAL 单元组成一个 RTP 包. 分别有4种组合方式: STAP-A, STAP-B, MTAP16, MTAP24. 那么这里的类型值分别是 24, 25, 26 以及 27.
3. 分片封包模式 用于把一个 NALU 单元封装成多个 RTP 包. 存在两种类型 FU-A 和 FU-B. 类型值分别是 28 和 29.
2.1 单一 NAL 单元模式
对于 NALU 的长度小于 MTU 大小的包, 一般采用单一 NAL 单元模式。
对于一个原始的 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 包即可.
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|F|NRI| Type | |
+-+-+-+-+-+-+-+-+ |
| |
| Bytes 2..n of a single NAL unit |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 1. RTP payload format for single NAL unit packet
这种一般适用于NAL包小于1460字节的情况,但不能太小,太小就需要合包,后面讨论;单个NAL单元包的rtp payload如上;
也就是说,直接将NAL头部和NAL数据放到rtp payload的位置就行,因为此时的rtp payload的头部和NAL头部是一样的,所以只需要用其中一个;
如有一个 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 个字节的开始码就可以了.
2.2 组合封包模式
其次, 当 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 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 2. An example of an RTP packet including an STAP-A containing two single-time aggregation units
这种一般是用于多个小的NAL包封装成一个大的NAL包,当然不超过1460字节;这种情况的rtp payload结构大致如上。
Note:
HDR=header 表示头部;
STAP-A NAL HDR其实就是rtp payload的头部的一个字节,值应该是00011000b(STAP-A类型=24,二进制即11000);
NALU size 占用两个字节;NALU 头部一个字节;注意,数据部分不是按32字节对齐的,同时NALU size不包含自身的2个字节;
2.3 Fragmentation Units (FUs).
而当 NALU 的长度超过 MTU 时, 就必须对 NALU 单元进行分片封包. 也称为 Fragmentation Units (FUs).
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 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 3. RTP payload format for FU-A
一个NAL包拆分成多个NAL包,这种一般是一个NAL包超过了1460字节的情况,这种情况的payload结构如上。
NOTE:
其中的FU indicator对应rtp payload的头部,此处应该是0x00011100b(FU-A=28,二进制表示即11100) ;
FU indicator定义如下:
+---------------+
|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 |
+---------------+
对于分片的第一个包,设置S为1;对于分片的最后一个包,设置E为1,R设置为0即可;Type字段对应的是NALU头部中的TYPE,可以用于区分帧类型;
注意,FU payload中并没有传送NALU的头部,NALU的头部由FU indicator(前3位)和FU header(后五位)组成:nal_unit_type = (fu_indicator & 0xe0) | (fu_header & 0x1f);
参考文章:
http://blog.csdn.net/heanyu/article/details/6109957(NRI描述参考)
http://www.cnblogs.com/frkang/p/3352251.html(RTP封包描述较清晰)
https://tools.ietf.org/html/rfc6184(标准,RTP Payload Format for H.264 Video)
http://m.blog.csdn.net/blog/yangguangmeng/25562921(I、P、B帧、SPS、PPS判断)
http://blog.csdn.net/ljzcom/article/details/9834405(杂)
http://www.360doc.com/content/13/0124/08/9008018_262076786.shtml(杂)
http://blog.csdn.net/jwybobo2007/article/details/7054140(组合封包例子貌似有错,STAP-A头不是78应该是24)
http://www.cnblogs.com/likwo/p/3533392.html(实践中需要注意的地方)
http://blog.sina.com.cn/s/blog_3f0f1d3701013mta.html(实例分析H264 RTP payload)