H264功能分为两层:视频编码层(VCL,VideoCodeing Layer)和网络提取层(NAL,Network Abstraction Layer)。VCL数据即编码处理的输出,它表示被压缩编码后的视频数据序列。在VCL数据传输或存储之前,这些编码的VCL数据,先被映射或封装在NAL单元中。每个NAL单元包括一个原始字节序列负荷(RBSP,Raw Byte Sequence Payload)、一组对应于视频编码的NAL头信息。RBSP的基本结构是:在原始编码数据的后面填充bit,1个bit“1”和若干bit“0”,以便字节对齐。NAL单元序列如图1所示。
图1 NAL单元序列
其中NAL头占用一个字节,如图2所示:包含1个bit的forbidden_zero_bit(F)、2个bit的nal_ref_idc(NRI)和5个比特的nal_unit_type(TYPE)。
图2 NAL头
F:H264规范声明设置为1指示语法为例。
NRI:00表示不是参考图像,可以丢弃;大于0的如果丢去则可能造成图像不完整。
TYPE:指示NAL类型,其取值如图3所示。
TYPE | NAL类型 |
0 | 未使用 |
1 | 不分区、非IDR图像的片 |
2 | 片分区A |
3 | 片分区B |
4 | 片分区C |
5 | IDR图像中的片 |
6 | 补充增强信息单元(SEI) |
7 | 序列参数集(SPS) |
8 | 图像参数集(PPS) |
9 | 分界符 |
10 | 序列结束 |
11 | 码流结束 |
12 | 填充 |
13-23 | 保留 |
24-31 | 未使用 |
图3 NAL单元类型
H264以字节流格式和RTP格式的码流结构如图4所示。
起始码:如果NAL单元对应的片(Slice)为一帧的开始,则用4个字节的0x00000001表示,否则用3个字节表示,0x000001。
脱壳操作:为了使NAL主体不包含起始码,在编码时没遇到连续两个字节的0x00,就插入一个字节的0x03,以和起始码相区别。解码时,则将相应0x03移除。
图4 H264码流结构
RTP封装h264结构的数据包包括[RTP头]+[h264载荷],其中h264载荷的第一个字节(载荷头)表示了使用什么结构来对H264数据进行封装。并且载荷头的结构和NAL头是一致的,根据载荷头的后5个字节可以知道具体使用的封装结构,如图5所示。其中,可能的结构包括:
单个NAL单元包:载荷中值包含一个NAL单元。载荷头类型域等于原始NAL单元类型,即范围在1-23之间。
聚合包:本类型用于聚合多个NAL单元到单个RTP载荷中。分为4类:单时间聚合包类型A(STAP-A),单时间聚合包类型B(STAP-B),多时间聚合包类型16位位移(MTAP16)、多时间聚合包类型24位位移(MTAP24),其载荷头类型大小分别是:24,25,26,27。
分片单元:用于分片单个NAL单元到多个RTP包。分为两类:FU-A、FU-B,其载荷头类型大小分别是:28,29。
TYPE | Packet |
0 | 未使用 |
1-23 | 单个NAL单元包 |
24 | STAP-A |
25 | STAP-B |
26 | MTAP16 |
27 | MTAP24 |
28 | FU-A |
29 | FU-B |
30-31 | 未使用 |
图5 RTP载荷头类型
RTP载荷头和NAL头重合,也就是说RTP载荷仅仅包含一个NAL单元,如图6所示
举例:一个NAl单元的数据为00 0000 01 67 23 34 ……,则使用单个NAL单元包的封装结构为[RTP头] 67 23 34 ……。
图6 单个NAL单元包
聚合相同NAL时间的NAL单元,且不包含DON(解码顺序号)。如图7所示,RTP载荷部分包括一个字节的RTP载荷头和若干NAL单元,其中每个NAL单元前包含2个字节的NAL长度。
举例:有两个NAL单元分别为00 00 00 01 67 12 34 56,00 00 00 01 68 23 5678 9A,则使用STAP-A的封装结构为[RTP头] 78 00 04 67 12 34 56 00 05 68 23 56 78 9A
图7 STAP-A
聚合相同NAL时间的NAL单元,且包含DON(解码顺序号)。图8所示,RTP载荷部分包括一个字节的RTP载荷头、2个字节DON和若干NAL单元,其中每个NAL单元前包含2个字节的NAL长度。
举例:有两个NAL单元分别为00 00 00 01 67 12 34 56,00 00 00 01 68 23 5678 9A,则使用STAP-B的封装结构为[RTP头] 78 [DON]00 04 67 12 34 56 00 05 68 23 56 78 9A,其中DON的获得暂不解释。
如8 STAP-B
聚合具有差异NAL时间的NAL单元。如图9所示,MTAP16的RTP载荷包括一个字节的载荷头、2个字节的DONB(解码顺序号基址)和若干NAL单元,其中每个NAL单元前包含2字节的NAL单元长度、1个字节的DOND(解码顺序号差值)、2个字节的时间戳位移(TSoffset)。
如图10所示,MTAP24的RTP载荷包括一个字节的载荷头、2个字节的DONB(解码顺序号基址)和若干NAL单元,其中每个NAL单元前包含2字节的NAL单元长度、1个字节的DOND(解码顺序号差值)、3个字节的时间戳位移(TS offset)。
图9 MTAP-16
图10 MTAP-24
将一个NAL单元分片到多个RTP包中。如图11所示,FU-A的RTP载荷包括1个字节的FUindicator、一个字节的FU header和NAL单元的一部分。如图12所示,FU-B的RTP载荷包括1个字节的FU indicator、1个字节的FU header、2个字节的DON和NAL单元的一部分。
图11 FU-A
图12 FU-B
其中FU-indicator的结构如图13所示:
F:和NAL头的F一致。
NRI:和NAL的NRI一致。
TYPE:28或者29。
图13FU-indicator
其中FU-header的结构如图14所示:
S:为1表示是分片的开始,否则为0。
E:为1表示分片的结束,否则为0。
TYPE:和NAL头的TYPE一致。
图14 FU-header
有多个FU-A/B分片获得完整的NAL单元:根据FU-header的S和E获得每一个分片,然后由FU-indicator的前3位和FU-header的后5位合成NAL头,再加上每一个分片的NAL单元的一部分就组成了完整的NAL单元。
注意:NAL头只需合成一次即可。
参考:
RTC3984
H264码流结构解析
新闻热点
疑难解答