H.264 NALU详解

目录

一、NALU概述

二、AnnexB 和 AVCC

2.1 AnnexB

2.2 AVCC

2.3 AnnexB和AVCC的区别

三、NALU结构

3.1 Nalu Header

3.2 Nalu Payload

3.2.1 防竞争字节

3.2.2 RBSP尾部

3.3 NALU解码过程

3.4 VCL NALU 和 非VCL NALU

3.3.1 VCL NALU

3.3.2 非VCL NALU

3.5 NALU和Slice

四、QA

1、防竞争字节,加入0x03之后就会成为0x00 00 03,如果数据本身就有 0x00 00 03怎么办?

2、AnnexB和AvcC有什么区别?

一、NALU概述

1、写在前面:学习的时候一定要参考官方文档,不同资料有歧义的地方以官方文档为准。

(1)官方文档:

        annexB格式(中文版可以参考 2005年3月这一版):H.264 : Advanced video coding for generic audiovisual services

        avcC格式:https://ossrs.io/lts/zh-cn/assets/files/ISO_IEC_14496-15-AVC-format-2012-345a5b466cc73e978fd9dd0840361e8b.pdf

2、H.264的原始码流(裸流)由一个个NALU(Network Abstraction Layer Unit)组成,它的功能分为两层:视频编码层(VCL)和网络提取层(NAL)。在VCL数据传输或存储之前,这些编码的VCL数据,先被映射或封装进NAL单元中。

(1)VCL(Video Coding Layer):编码器处理的输出,它表示被压缩编码后的视频数据序列。包括核心压缩引擎块、宏块和片的语法级别定义,设计目标是尽可能独立于网络进行高效的编码。

(2)NAL(Network Abstract Layer):负责将VCL产生的比特字符串适配到各种各样的网络和多元环境中,覆盖了所有片界别以上的语法级别。简单来说就是将编码完毕的内容打包成符合H.264 格式数据流;

3、一帧图片经过H.264编码器之后,就被编码为一个或多个片(slice),每个slice最后都将产生一个NALU。 (【精选】H264 NALU说明_大飞飞鱼的博客-CSDN博客)但是并不是NALU内就一定是切片,因为NALU还有可能装载着其它用作描述视频的信息。NALU由nalu header和nalu payload两个部分组成:

(1)nalu header:用来表示数据类型。

(2)nalu payload:存储了真正的数据。

4、目前视频中的H.264流行的NALU包装方式有两种,一种叫做annexB,一种叫做avcC。对于这两种格式,不同的厂商支持程度也不太一样,例如,Android硬解码MediaCodec只接受annexB格式的数据,而Apple的VideoToolBox,只支持avcC的格式。

5、NALU的作用:NALU的最核心的用处就是起到一个承上启下的作用。我们知道,在通常情况下,H.264 码流只存放了原始的画面信息,而不包含其他信息,所以严格来说,H.264 码流和我们通常意义上的“视频”还是有一定差距的。比如,我们通常说的视频要包含音频,H.264 码流里就没有。除了音频之外,我们还需要存放其他辅助信息,比如视频的播放时长,每一帧的播放时间,帧率,画面是否旋转,等等非常多的信息。所以我们日常使用过程中,往往是把 H.264 码流插入到其他视频容器内,比如 mp4,flv等。而 NALU 的作用,就是很好的把 H.264 码流和上层的封装容器进行有效得分割,使得两者可以相互不干扰,相互独立。(什么是 NALU-ZigZagSin

6、大多情况下,NALU在网络发送时,会封装成RTP格式,有两种方式:

(1)单包模式:rtp payload仅由一个完整NALU组成,这种情况一般用于H264 NALU大小 小于MTU(Maximum Transmission Unit,最大传输单元)。SPS、PPS采用单包模式发送。

(2)分包模式:当H264 NALU 的长度超过MTU时, 就必须对NALU进行分片封包,也称为 Fragmentation Units。

二、AnnexB 和 AVCC

1、目前视频中的H.264流行的NALU包装格式有两种:annexB和avcC。

(1)avcC格式也叫AVC1格式,MPEG-4格式,字节对齐,因此也叫Byte-Stream Format。用于mp4、flv、mkv等封装中。 
(2)annexB格式也叫MPEG-2 transport stream format格式(ts格式),ElementaryStream格式。用于ts流中(以及使用ts作为切片的hls格式中即".ts"文件中)。

2、对于annexB和avcC这两种格式,不同的厂商支持程度也不太一样,例如,Android硬解码MediaCodec只接受annexB格式的数据,而Apple的VideoToolBox,只支持avcC的格式。

2.1 AnnexB

1、annexB的格式是在NALU前面添加Start Code(起始码),用于表示一个NALU的开始。Start Code的内容为三字节的0x00 00 01或者4字节的0x00 00 00 01(其中起始码在NALU为SPS、PPS或NALU为一帧的第一个NALU时使用4字节,其他情况使用3字节)(H264码流之AnnexB和AVCC

2、annexB格式下不同帧发送顺序,发送第一个I帧之前,至少要发送一次SPS/PPS。如果码流没有改变,则只发送一次SPS/PPS即可,不用每一个I帧之前都发送SPS/PPS。但是如果码流中间有变,比如分辨率、帧率或者其它编码参数有改变,则要重新发送SPS/PPS。

2、但是原始码流中是可能出现和起始码一样的数据,这样就会导致错误的NALU分割。为了防止这种情况发生,AnnexB引入了防竞争字节(Emulation Prevention Bytes)的概念。具体操作为:编码器编完一个NALU后,检查内部是否出现如下左侧的字节序列,如果存在,则在最后一个字节前插入一个新的字节0x03

0x00 00 00 => 0x00 00 03 00
0x00 00 01 => 0x00 00 03 01
0x00 00 02 => 0x00 00 03 02
0x00 00 03 => 0x00 00 03 03

其中,0x00 00 00对应start code=0x00 00 00 01的情况,0x00 00 01对应start code=0x00 00 01的情。0x00 00 02是作为保留使用,而0x00 00 03,则是为了防止NALU内部,原本就有序列为0x00 00 03这样的数据。(【H264/AVC 句法和语义详解】(三):NALU详解二(EBSP、RBSP与SODB) - 简书

)防止插入0x03后形成的0x00 00 03和原本数据的0x00 00 03相同而造成混淆。

3、解码器在NALU内部检测到防竞争字节后将0x03丢弃来恢复原始数据。

2.2 AVCC

1、在一路采用avcC打包的H.264流之中,我们首先看到的将是一段被称之为 extradata 的数据,这段数据定义了这个 H.264 流的基本属性数据,当然,也包含了 SPS 和 PPS 数据。

2、avcC的格式是在每一个NALU前加上了一个指定其长度(NALU包大小)的前缀(大端字节序),前缀的大小可以是1、2或4字节,每一个NALU的前缀大小都是相同的,只是前缀的内容不同。前缀大小保存在一个头部对象中(流开始的部分),这个头部对象通常称为"extradata"或者"sequence header"(非NALU)。解析的时候先从extradata(sequence header)读取NALU前缀的大小,然后再读区前缀的内容获取NALU的长度。

3、extradata(或sequence header)中包含NALU长度的字节数以及SPS/PPS信息。的基本格式如下:

Field

Type

Comment

version8 bit总是等于 0x01
avc profile8 bit所存放第一个 SPS 的第一个字节
avc compatibility8 bit所存放第一个 SPS 的第二个字节
avc level8 bit所存放第一个 SPS 的第三个字节
reserved6 bit保留字段
NALULengthSizeMinusOne2 bitNalu前缀长度减1,值如果是3,那前缀就是4,因为4-1=3
reserved

3 bit

保留字段
number of SPS NALUs5 bit有几个 SPS,一般情况下这里是 1
for(int i=0; i<number of SPS NALUs; i++) {~
        SPS size16 bitSPS 的长度
        SPS NALU dataSPS NALU 的数据
}~
number of PPS NALUs8 bit有几个 PPS,一般情况下这里是 1
for(int i=0; i<number of PPS NALUs; i++) {~
        PPS size16 bitPPS 的长度
        PPS NALU dataPPS NALU 的数据
}~

其中,NALULengthSizeMinusOne用来描述NALU前缀的长度。NALULengthSizeMinusOne这个字段长度是2 bit,也就是说,这个字段可以存放的数据范围是0到4。所以,NALU前缀的长度就是1个字节到5个字节。(是NALU前缀的前度,不是前缀的内容)如果NALULengthSizeMinusOne的值是3,那么每个NALU前缀长度就是3+1=4个字节。在读取数据时,可以先读4个字节,然后把这4个字节内容转成整数,就是这个NALU的长度。注意,这个长度并不包含NALU前缀的4个字节,是单纯NALU的长度。

avcC示例:

4、虽然avcC格式不使用起始码,防竞争字节还是有的。

2.3 AnnexB和AVCC的区别

1、NALU的分割方式不同

        annexB格式使用start code进行分割,start code为0x000001或0x00000001。

        avcC格式使用NALU长度(固定字节,字节数由extradata中的信息给定)进行分割。

2、SPS/PPS的数据结构不同

        annexB格式中,SPS/PPS存放在NALU中,以start code作为分隔符。这种格式下SPS/PPS通常放在关键帧之前。

        avcC格式中,SPS/PPS存放在extradata中,不是放在NALU中。这种格式下SPS/PPS的存放位置和帧数据无关。例如普通模式下的mp4,SPS/PPS放在MOOV中,在帧数据后面。

3、适用场景不同

        annexB格式通常用于实时的流格式,比如说传输流,通过无线传输的广播、DVD等。在这些格式中通常会周期性的重复SPS和PPS包,经常是在每一个关键帧之前。

        avcC格式的一个优点是在开始配置解码器的时候可以跳到流的中间播放,这种格式通常用于可以被随机访问的多媒体数据,如存储在硬盘的文件。也因为这个特性,MP4、FLV、MKV通常用avcC格式来存储。(H.264流媒体协议格式中的Annex B格式和AVCC格式深度解析-CSDN博客

三、NALU结构

1、annexB和avcC两种格式下的NALU结构是相同的。

2、NALU由nalu header和nalu payload两个部分组成:

(1)nalu header:一个字节的头信息,用来表示数据类型。

(2)nalu payload:存储了真正的数据。

3、nalu结构如下图所示:

3.1 Nalu Header

1、nalu header的长度固定是一个字节,用来表示数据类型。

2、主要内容如下:

Field

Type

Comment

forbidden_zero_bit

(禁止位)

1 bit

在H.264规范中规定了这一位必须是0

nal_ref_idc

(重要性指示位)

2 bit

        用于表示当前NALU的重要性,值越大,越重要。解码器在解码处理不过来的时候,可以丢掉重要性为0的NALU。

        nal_ref_idc不等于0时,NAL unit的内容可能是 SPS/PPS/参考帧 的片

        nal_ref_idc等于0时,NAL unit的内容可能是非参考图像的片

        当某个图像的片的nal_ref_id等于0时,该图像的所有片均应等于0

        当nal_uint_type为1时,nal_ref_idc代表该帧是否被参考,若该帧没有被参考,nal_ref_idc则为0。在视频传输过程中,对于P帧或者B帧的nal_ref_idc为0的帧,不进行数据恢复时,不会对后续视频有影响。(h264编码笔记(nal_ref_idc)_CrystalShaw的博客-CSDN博客

nal_unit_type

(负荷数据类型)

5 bit

是指包含在NAL单元中的RBSP数据结构的类型。VCL NAL单元是指那些 nal_unit_type值等于1到5(包括1和5)的NAL单元。所有其他的NAL单元都称作非VCL NAL单元。

3、nal_unit_type内容如下:(H.264官方文档2005.03版)

需要注意的几个值,他们在编码解码中需要特别注意:

(1)从上表可知,nal_unit_type=1~5时表示RBSP里面包含的数据为条带(片/Slice)数据,所以值为1~5的NALU统称为VCL(视像编码层)NALU,其他的NALU则称为非VCL NALU。

(2)nal_unit_type=5时为I帧,nal_unit_type=1时为P帧或B帧。

(3)nal_unit_type=6时为SEI数据,当任何SEI NAL单元存在时,他们应该在基本编码图像之前。

如下图,SEI在I帧之前

(4)nal_unit_type=7时为SPS(序列参数集)。

(5)nal_unit_type=8时为PPS(图像参数集)。

(6)nal_unit_type=9时为AU分割符(访问单元分隔符)。有时候用于解码中的帧边界识别。因为一帧数据由一个或多个NALU组成,即来自同一帧的一个或多个NALU组成了一个Access Units(AU),AU包含了一个完整的帧。把帧分割成几个独立的NALU需要耗费许多CPU资源,所以分割帧数据并不经常使用。实际上AU分隔不常用。

3.2 Nalu Payload

1、nalu payload也叫RBSP(raw byte sequence payload)。 RBSP是原始字节序列载荷,官方解释:一个语法结构,包含整数个封装于NAL单元中的字节。RBSP或者为空,或者包含具有数据比特串形式的语法元素,其后跟随 RBSP 截止位和零个 或多个连续的 0 值比特。

2、RBSP包含一个SODB(string of data bits)。SODB是数据比特串,官方解释:表示语法元素的若干比特位的序列,出现在原始字节序列荷载中原始字节序列荷载结束位之前。在SODB中,最左边的比特位是第一位并且是最高位,最右边的比特位则是最后一位且是最低位。

3、通俗来讲SODB就是编码器输出的数据,也就是前文中提到的VCL(Video Coding Layer)。按照一定规则,对SODB做一定的处理就是RBSP。SODB处理成RBSP的过程如下:(1)如果SODB为空(例如:长度是0比特),RBSP也为空。

(2)如果SODB不为空则会出现防竞争字节和字节补齐等操作。

3.2.1 防竞争字节

1、NALU的起始码为0x00 00 01或0x00 00 00 01,同时H264规定,当检测到0x00 00 00时,也可以表示当前NALU的结束。那这样就会产生一个问题,就是如果在NALU的内部,出现了0x00 00 01或0x00 00 00时该怎么办?所以H264就提出了“防止竞争”这样一种机制,当编码器编码完一个NALU时,应该检测NALU内部,是否出现0x00 00 xx 这样的序列(其中 xx 代表任意2比特图案:00、01、10或11)。当检测到它们存在时,编码器就在最后一个字节前,插入一个新的字节:0x03。即

0x00 00 00 => 0x00 00 03 00
0x00 00 01 => 0x00 00 03 01
0x00 00 02 => 0x00 00 03 02
0x00 00 03 => 0x00 00 03 03

其中,0x00 00 00对应start code=0x00 00 00 01的情况,0x00 00 01对应start code=0x00 00 01的情。0x00 00 02是作为保留使用,而0x00 00 03,则是为了防止NALU内部,原本就有序列为0x00 00 03这样的数据,防止插入0x03后形成的0x00 00 03和原本数据的0x00 00 03相同而造成混淆。

【H264/AVC 句法和语义详解】(三):NALU详解二(EBSP、RBSP与SODB) - 简书

3.2.2 RBSP尾部

1、RBSP的第一个字节包括(最高位的,最左边的)8比特的SODB;RBSP的下一个字节应包括接下来 的8比特的SODB,等等,直到剩下的SODB少于8比特。此时会将最后不满一个字节的情况进行补齐,先写入1 bit数据,数据内容是1,然后开始补齐0,直到补齐到一整个字节。如果SODB恰好填满最后一个字节,也会进行补齐,也会先写入1 bit数据,数据内容是1,然后开始补齐0,直到补齐到一整个字节。

2、在补齐子节之后,如果数据为条带(片/Slice)数据(nal_unit_type=1~5),可能还会再进行补齐,补齐一个或多个0x0000(cabac_zero_word)。

3、NALU的最后一个字节不能等于 0x00,所以当RBSP数据的最后一个字节等于0x00(只有在RBSP以cabac_zero_word结尾时才会出现),在数据的末尾添加一个等于0x03的字节。

4、该过程允许任何SODB在一个NALU中出现,同时可以确保

(1)该NAL单元中没有字节对齐的伪起始码。

(2)无论是否字节对齐,在NAL单元中没有8个值为0的比特后跟随起始码的序列。

5、经过以上分析,NALU的结构可以见下图

3.3 NALU解码过程

1、在进行 NALU解码之前,首先或者通过RTP协议解析(采用 RTP 封装),或者通过起始码检测(采用比特流方式),从传输码流中获取NALU数据。

2、NALU解码的总体流程是:首先从NALU中提取出RBSP语法结构,然后按照下图所示的流程处理RBSP语法结构。因此对于 NAL 单元的解码过程,其输入是NALU,输出结果是经过解码的当前图像(CurrPic)的样点值。在 H.264 规范文档中规定:对于同一码流,所有的解码器必须产生数值上相同的结果,且必须符合 规范定义的解码过程的标准。

nal_unit_type为7、8的NALU中分别包含了序列参数集(SPS)和图像参数集(PPS)。SPS、PPS在其它数据NALU的解码过程中作为参数使用,在这些数据NALU的片头中通过语法元素 pic_parameter_set_id设置它们使用的PPS编号,而相应的每个PPS中通过语法元素 seq_parameter_set_id设置它所使用的SPS编号。

3.4 VCL NALU 和 非VCL NALU

1、vcl nalu(nal_unit_type=1~5)中存储条带(片/Slice)数据(真正的视频数据),如I、P、B帧的数据。非vcl nalu中存储的是描述信息,如SPS或PPS等。

3.3.1 VCL NALU

1、包含帧的数据,主要是宏块的信息.

2、A类型分块(头信息块):包括宏块类型、量化参数、运动矢量等信息。(H264编码基本原理(一)_h.264编码原理-CSDN博客

3、B类型分块(帧内编码信息数据块):包含帧内编码宏块类型,帧内编码系数。对于slice来说,B类数据分块的可用性依赖A类数据分块。

4、C类型分块(帧间编码信息数据块):包含帧间编码宏块类型,帧间编码系数。它通常是slice中最大的一部分。B类数据分块的可用性依赖A类数据分块,但对于B类数据分块无关。

3.3.2 非VCL NALU

1、SPS(序列参数集):对应的是针对一段连续编码视频序列的参数。包含 帧数、POC的约束、参考帧数目、解码图像尺寸和帧场编码模式选择标识等信息。

2、PPS(图像参数集):对应的是一个序列中某一副图像或者某几幅图像的参数。包含 熵编码模式选择标识、片组数目、初始量化参数和去方块滤波系数调整标识等信息。

(SPS、PPS中包含的详细可参考:H.264 SPS、PPS详解-CSDN博客

3.5 NALU和Slice

1、一帧图片经过H.264编码器之后,就被编码为一个或多个片(slice),而装载着这些片(slice)的载体,就是NALU ,每一个slice都对应一个NALU。

2、slice数据就是NALU中的SODB数据。

3、slice的主要作用是用作宏块(Macroblock)的载体。slice之所以被创造出来,主要目的是为限制误码的扩散和传输。

4、每个slice都应该是互相独立被传输的,某片的预测(片(slice)内预测和片(slice)间预测)不能以其它片中的宏块(Macroblock)为参考图像。(H264 NALU详细分析3 关于slice 关于RBSP 关于是否带Start Code 也许是终章了_nalu和slice_杀神李的博客-CSDN博客

5、slice包含slice header和slice data,语法结构如下,

(1)slice header:包含着分片类型、分片中的宏块类型、分片帧的数量、分片属于那个图像以及对应的帧的设置和参数等信息。

(2)slice data:包含一个或多个宏块,这里就是我们要找的存储像素数据的地方。

(3)包含有slice的RBSP的尾部应该使用“带条带比特RBSP语法”(详见“3.2.2 RBSP尾部”)。

四、QA

1、防竞争字节,加入0x03之后就会成为0x00 00 03,如果数据本身就有 0x00 00 03怎么办?

原本的0x00 00 03也会再插入防竞争字节,插入后会变成0x00 00 03 03(如下所示)。防止插入0x03后形成的0x00 00 03和原本数据的0x00 00 03相同而造成混淆。即使原本就有0x00 00 03 03这种数据,加入防竞争字节后会变成0x00 00 03 03 03。

0x00 00 03 => 0x00 00 03 03

2、AnnexB和AvcC有什么区别?

详见 “2.3 AnnexB和AVCC的区别”

  • 7
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值