h264
前言
-----------------------
H264是新一代的编码标准,以高压缩高质量和支持多种网络的流媒体传输著称,在编码方面,我理解的他的理论依据是:参照一段时间内图像的统计结果表明,在相邻几幅图像画面中,一般有差别的像素只有10%以内的点,亮度差值变化不超过2%,而色度差值的变化只有1%以内。所以对于一段变化不大图像画面,我们可以先编码出一个完整的图像帧A,随后的B帧就不编码全部图像,只写入与A帧的差别,这样B帧的大小就只有完整帧的1/10或更小!B帧之后的C帧如果变化不大,我们可以继续以参考B的方式编码C帧,这样循环下去。这段图像我们称为一个序列(序列就是有相同特点的一段数据),当某个图像与之前的图像变化很大,无法参考前面的帧来生成,那我们就结束上一个序列,开始下一段序列,也就是对这个图像生成一个完整帧A1,随后的图像就参考A1生成,只写入与A1的差别内容。
在H264协议里定义了三种帧,完整编码的帧叫I帧,参考之前的I帧生成的只包含差异部分编码的帧叫P帧,还有一种参考前后的帧编码的帧叫B帧。
H264采用的核心算法是帧内压缩和帧间压缩,帧内压缩是生成I帧的算法,帧间压缩是生成B帧和P帧的算法。
----------------------
序列的说明
----------------------
在H264中图像以序列为单位进行组织,一个序列是一段图像编码后的数据流,以I帧开始,到下一个I帧结束。
一个序列的第一个图像叫做 IDR 图像(立即刷新图像),IDR 图像都是 I 帧图像。H.264 引入 IDR 图像是为了解码的重同步,当解码器解码到 IDR 图像时,立即将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始一个新的序列。这样,如果前一个序列出现重大错误,在这里可以获得重新同步的机会。IDR图像之后的图像永远不会使用IDR之前的图像的数据来解码。
一个序列就是一段内容差异不太大的图像编码后生成的一串数据流。当运动变化比较少时,一个序列可以很长,因为运动变化少就代表图像画面的内容变动很小,所以就可以编一个I帧,然后一直P帧、B帧了。当运动变化多时,可能一个序列就比较短了,比如就包含一个I帧和3、4个P帧。
-----------------------
三种帧的说明
-----------------------
I帧:帧内编码帧 ,I帧表示关键帧,你可以理解为这一帧画面的完整保留;解码时只需要本帧数据就可以完成(因为包含完整画面)
I帧特点:
1.它是一个全帧压缩编码帧。它将全帧图像信息进行JPEG压缩编码及传输;
2.解码时仅用I帧的数据就可重构完整图像;
3.I帧描述了图像背景和运动主体的详情;
4.I帧不需要参考其他画面而生成;
5.I帧是P帧和B帧的参考帧(其质量直接影响到同组中以后各帧的质量);
6.I帧是帧组GOP的基础帧(第一帧),在一组中只有一个I帧;
7.I帧不需要考虑运动矢量;
8.I帧所占数据的信息量比较大。
P帧:前向预测编码帧。P帧表示的是这一帧跟之前的一个关键帧(或P帧)的差别,解码时需要用之前缓存的画面叠加上本帧定义的差别,生成最终画面。(也就是差别帧,P帧没有完整画面数据,只有与前一帧的画面差别的数据)
P帧的预测与重构:P帧是以I帧为参考帧,在I帧中找出P帧“某点”的预测值和运动矢量,取预测差值和运动矢量一起传送。在接收端根据运动矢量从I帧中找出P帧“某点”的预测值并与差值相加以得到P帧“某点”样值,从而可得到完整的P帧。
P帧特点:
1.P帧是I帧后面相隔1~2帧的编码帧;
2.P帧采用运动补偿的方法传送它与前面的I或P帧的差值及运动矢量(预测误差);
3.解码时必须将I帧中的预测值与预测误差求和后才能重构完整的P帧图像;
4.P帧属于前向预测的帧间编码。它只参考前面最靠近它的I帧或P帧;
5.P帧可以是其后面P帧的参考帧,也可以是其前后的B帧的参考帧;
6.由于P帧是参考帧,它可能造成解码错误的扩散;
7.由于是差值传送,P帧的压缩比较高。
B帧:双向预测内插编码帧。B帧是双向差别帧,也就是B帧记录的是本帧与前后帧的差别(具体比较复杂,有4种情况,但我这样说简单些),换言之,要解码B帧,不仅要取得之前的缓存画面,还要解码之后的画面,通过前后画面的与本帧数据的叠加取得最终的画面。B帧压缩率高,但是解码时CPU会比较累。
B帧的预测与重构
B帧以前面的I或P帧和后面的P帧为参考帧,“找出”B帧“某点”的预测值和两个运动矢量,并取预测差值和运动矢量传送。接收端根据运动矢量在两个参考帧中“找出(算出)”预测值并与差值求和,得到B帧“某点”样值,从而可得到完整的B帧。
B帧特点
1.B帧是由前面的I或P帧和后面的P帧来进行预测的;
2.B帧传送的是它与前面的I或P帧和后面的P帧之间的预测误差及运动矢量;
3.B帧是双向预测编码帧;
4.B帧压缩比最高,因为它只反映丙参考帧间运动主体的变化情况,预测比较准确;
5.B帧不是参考帧,不会造成解码错误的扩散。
注:I、B、P各帧是根据压缩算法的需要,是人为定义的,它们都是实实在在的物理帧。一般来说,I帧的压缩率是7(跟JPG差不多),P帧是20,B帧可以达到50。可见使用B帧能节省大量空间,节省出来的空间可以用来保存多一些I帧,这样在相同码率下,可以提供更好的画质。
--------------------------------
压缩算法的说明
--------------------------------
h264的压缩方法:
1.分组:把几帧图像分为一组(GOP,也就是一个序列),为防止运动变化,帧数不宜取多。
2.定义帧:将每组内各帧图像定义为三种类型,即I帧、B帧和P帧;
3.预测帧:以I帧做为基础帧,以I帧预测P帧,再由I帧和P帧预测B帧;
4.数据传输:最后将I帧数据与预测的差值信息进行存储和传输。
帧内(Intraframe)压缩也称为空间压缩(Spatial compression)。当压缩一帧图像时,仅考虑本帧的数据而不考虑相邻帧之间的冗余信息,这实际上与静态图像压缩类似。帧内一般采用有损压缩算法,由于帧内压缩是编码一个完整的图像,所以可以独立的解码、显示。帧内压缩一般达不到很高的压缩,跟编码jpeg差不多。
帧间(Interframe)压缩的原理是:相邻几帧的数据有很大的相关性,或者说前后两帧信息变化很小的特点。也即连续的视频其相邻帧之间具有冗余信息,根据这一特性,压缩相邻帧之间的冗余量就可以进一步提高压缩量,减小压缩比。帧间压缩也称为时间压缩(Temporal compression),它通过比较时间轴上不同帧之间的数据进行压缩。帧间压缩一般是无损的。帧差值(Frame differencing)算法是一种典型的时间压缩法,它通过比较本帧与相邻帧之间的差异,仅记录本帧与其相邻帧的差值,这样可以大大减少数据量。
顺便说下有损(Lossy )压缩和无损(Lossy less)压缩。无损压缩也即压缩前和解压缩后的数据完全一致。多数的无损压缩都采用RLE行程编码算法。有损压缩意味着解压缩后的数据与压缩前的数据不一致。在压缩的过程中要丢失一些人眼和人耳所不敏感的图像或音频信息,而且丢失的信息不可恢复。几乎所有高压缩的算法都采用有损压缩,这样才能达到低数据率的目标。丢失的数据率与压缩比有关,压缩比越小,丢失的数据越多,解压缩后的效果一般越差。此外,某些有损压缩算法采用多次重复压缩的方式,这样还会引起额外的数据丢失。
前言
-------------
H264结构中,一个视频图像编码后的数据叫做一帧,一帧由一个片(slice)或多个片组成,一个片由一个或多个宏块(MB)组成,一个宏块由16x16的yuv数据组成。宏块作为H264编码的基本单位。
-------------------------
-----------------------------------------------
H264/AVC 的分层结构
-----------------------------------------------
H.264的主要目标是:
1.高的视频压缩比;
2.良好的网络亲和性;
为了完成这些目标H264的解决方案是:
1.VCL video coding layer 视频编码层;
2.NAL network abstraction layer 网络提取层;
其中,VCL层是对核心算法引擎,块,宏块及片的语法级别的定义,他最终输出编码完的数据 SODB;
NAL层定义片级以上的语法级别(如序列参数集和图像参数集,针对网络传输),同时支持以下功能:独立片解码,起始码唯一保证,SEI以及流格式编码数据传送,NAL层将SODB打包成RBSP然后加上NAL头,组成一个NALU(NAL单元);
---------------------------------------------
H264网络传输的结构
---------------------------------------------
H264在网络传输的是NALU,NALU的结构是:NAL头+RBSP,实际传输中的数据流如图所示:
从前面的分析我们知道,VCL层出来的是编码完的视频帧数据,这些帧可能是I、B、P帧,而且这些帧可能属于不同的序列,再者同一个序列还有相对应的一套序列参数集和图片参数集等等,所以要完成视频的解码,不仅需要传输VCL层编码出来的视频帧数据,还需要传输序列参数集、图像参数集等数据。
NALU头用来标识后面的RBSP是什么类型的数据,他是否会被其他帧参考以及网络传输是否有错误。
RBSP用来存放下表中的一种:
RBSP类型 | 所写 | 描述 |
参数集 | PS | 序列的全局信息,如图像尺寸,视频格式等 |
增强信息 | SEI | 视频序列解码的增强信息 |
图像界定符 | PD | 视频图像的边界 |
编码片 | SLICE | 编码片的头信息和数据 |
数据分割 | DP片层的数据,用于错误恢复解码 | |
序列结束符 | 表明一个序列的结束,下一个图像为IDR图像 | |
流结束符 | 表明该流中已没有图像 | |
填充数据 | 亚元数据,用于填充字节 |
其中,
参数集:包括序列参数集 SPS 和图像参数集 PPS
SPS 包含的是针对一连续编码视频序列的参数,如标识符 seq_parameter_set_id、帧数及 POC 的约束、参考帧数目、解码图像尺寸和帧场编码模式选择标识等等。
PPS对应的是一个序列中某一幅图像或者某几幅图像,其参数如标识符 pic_parameter_set_id、可选的 seq_parameter_set_id、熵编码模式选择标识、片组数目、初始量化参数和去方块滤波系数调整标识等等。
数据分割:组成片的编码数据存放在 3 个独立的 DP(数据分割,A、B、C)中,各自包含一个编码片的子集。分割A包含片头和片中每个宏块头数据。分割B包含帧内和 SI 片宏块的编码残差数据。分割 C包含帧间宏块的编码残差数据。每个分割可放在独立的 NAL 单元并独立传输。
-----------------------------------------
NALU头结构
----------------------------------------
NALU头结构:nal_unit_type(5bit)+nal_reference_bit(2bit)+forbidden_bit(1bit)
1.nal_unit_type:NALU类型取值如下表所示。
nal_unit_type |
NAL类型 |
C |
0 |
未使用 |
|
1 |
非IDR图像中不采用数据划分的片段 |
2,3,4 |
2 |
非IDR图像中A类数据划分片段 |
2 |
3 |
非IDR图像中B类数据划分片段 |
3 |
4 |
非IDR图像中C类数据划分片段 |
4 |
5 |
IDR图像的片 |
2,3 |
6 |
补充增强信息单元(SEI) |
5 |
7 |
序列参数集 |
0 |
8 |
图像参数集 |
1 |
9 |
分界符 |
6 |
10 |
序列结束 |
7 |
11 |
码流结束 |
8 |
12 |
填充 |
9 |
13..23 |
保留 |
|
24..31 |
不保留 |
2.nal_reference_bit:nal重要性指示,标志该NAL单元的重要性,值越大,越重要,解码器在解码处理不过来的时候,可以丢掉重要性为0的NALU。不同类型的NALU的重要性指示如下表所示。
nal_unit_type |
NAL类型 |
nal_reference_bit |
0 |
未使用 |
0 |
1 |
非IDR的片 |
此片属于参考帧,则不等于0, 不属于参考帧,则等与0 |
2 |
片数据A分区 |
同上 |
3 |
片数据B分区 |
同上 |
4 |
片数据C分区 |
同上 |
5 |
IDR图像的片 |
5 |
6 |
补充增强信息单元(SEI) |
0 |
7 |
序列参数集 |
非0 |
8 |
图像参数集 |
非0 |
9 |
分界符 |
0 |
10 |
序列结束 |
0 |
11 |
码流结束 |
0 |
12 |
填充 |
0 |
13..23 |
保留 |
0 |
24..31 |
不保留 |
0 |
所谓参考帧,就是在其他帧解码时需要参照的帧。比如一个I帧可能被一个或多个B帧参考,一个B帧可能被某个P帧参考。
从这个表我们也可以看出来,DIR的I帧是非常重要的,他一丢,那么这个序列的所有帧都没办法解码了;然后序列参数集和图像参数集也很重要,没有序列参数集,这个序列的帧就没法解;没有图像参数集,那用到这个图像参数集的帧都没法解。
3.forbidden_bit:禁止位,初始为0,当网络发现NAL单元有比特错误时可设置该比特为1,以便接收方纠错或丢掉该单元。
-------------------------------------
NAL的开始和结束
-------------------------------
编码器将每个NAL各自独立、完整地放入一个分组,因为分组都有头部,解码器可以方便地检测出NAL的分界,并依次取出NAL进行解码。每个NAL前有一个起始码 0x00 00 01(或者0x00 00 00 01),解码器检测每个起始码,作为一个NAL的起始标识,当检测到下一个起始码时,当前NAL结束。同时H.264规定,当检测到0x000000时,也可以表征当前NAL的结束。那么NAL中数据出现0x000001或0x000000时怎么办?H.264引入了防止竞争机制,如果编码器检测到NAL数据存在0x000001或0x000000时,编码器会在最后个字节前插入一个新的字节0x03,这样:
0x000000->0x00000300
0x000001->0x00000301
0x000002->0x00000302
0x000003->0x00000303
解码器检测到0x000003时,把03抛弃,恢复原始数据。解码器在解码时,首先逐个字节读取NAL的数据,统计NAL的长度,然后再开始解码。
--------------------------------------
NALU的顺序要求
--------------------------------------
H.264/AVC标准对送到解码器的NAL单元顺序是有严格要求的,如果NAL单元的顺序是混乱的,必须将其重新依照规范组织后送入解码器,否则解码器不能够正确解码。
1.序列参数集NAL单元 必须在传送所有以此参数集为参考的其他NAL单元之前传送,不过允许这些NAL单元中间出现重复的序列参数集NAL单元。
所谓重复的详细解释为:序列参数集NAL单元都有其专门的标识,如果两个序列参数集NAL单元的标识相同,就可以认为后一个只不过是前一个的拷贝,而非新的序列参数集。
2.图像参数集NAL单元 必须在所有以此参数集为参考的其他NAL单元之前传送,不过允许这些NAL单元中间出现重复的图像参数集NAL单元,这一点与上述的序列参数集NAL单元是相同的。
3.不同基本编码图像中的片段(slice)单元和数据划分片段(data partition)单元在顺序上不可以相互交叉,即不允许属于某一基本编码图像的一系列片段(slice)单元和数据划分片段(data partition)单元中忽然出现另一个基本编码图像的片段(slice)单元片段和数据划分片段(data partition)单元。
4.参考图像的影响:如果一幅图像以另一幅图像为参考,则属于前者的所有片段(slice)单元和数据划分片段(data partition)单元必须在属于后者的片段和数据划分片段之后,无论是基本编码图像还是冗余编码图像都必须遵守这个规则。
5.基本编码图像的所有片段(slice)单元和数据划分片段(data partition)单元必须在属于相应冗余编码图像的片段(slice)单元和数据划分片段(data partition)单元之前。
6.如果数据流中出现了连续的无参考基本编码图像,则图像序号小的在前面。
7.如果arbitrary_slice_order_allowed_flag置为1,一个基本编码图像中的片段(slice)单元和数据划分片段(data partition)单元的顺序是任意的,如果arbitrary_slice_order_allowed_flag置为零,则要按照片段中第一个宏块的位置来确定片段的顺序,若使用数据划分,则A类数据划分片段在B类数据划分片段之前,B类数据划分片段在C类数据划分片段之前,而且对应不同片段的数据划分片段不能相互交叉,也不能与没有数据划分的片段相互交叉。
8.如果存在SEI(补充增强信息)单元的话,它必须在它所对应的基本编码图像的片段(slice)单元和数据划分片段(data partition)单元之前,并同时必须紧接在上一个基本编码图像的所有片段(slice)单元和数据划分片段(data partition)单元后边。假如SEI属于多个基本编码图像,其顺序仅以第一个基本编码图像为参照。
9.如果存在图像分割符的话,它必须在所有SEI 单元、基本编码图像的所有片段slice)单元和数据划分片段(data partition)单元之前,并且紧接着上一个基本编码图像那些NAL单元。
10.如果存在序列结束符,且序列结束符后还有图像,则该图像必须是IDR(即时解码器刷新)图像。序列结束符的位置应当在属于这个IDR图像的分割符、SEI 单元等数据之前,且紧接着前面那些图像的NAL单元。如果序列结束符后没有图像了,那么它的就在比特流中所有图像数据之后。
11.流结束符在比特流中的最后。
------------------------------
H.264的NAL层处理
------------------------------
H264以NALU(NAL unit)为单位来支持编码数据在基于分组交换技术网络中传输。
NALU定义了可用于基于分组和基于比特流系统的基本格式,同时给出头信息,从而提供了视频编码和外部世界的接口。
H264编码过程中的三种不同的数据形式:
SODB 数据比特串-->最原始的编码数据,即VCL数据;
RBSP 原始字节序列载荷-->在SODB的后面填加了结尾比特(RBSP trailing bits 一个bit“1”)若干比特“0”,以便字节对齐;
EBSP 扩展字节序列载荷-->在RBSP基础上填加了仿校验字节(0X03)它的原因是: 在NALU加到Annexb上时,需要添加每组NALU之前的开始码StartCodePrefix,如果该NALU对应的slice为一帧的开始则用4位字节表示,ox00000001,否则用3位字节表示ox000001(是一帧的一部分)。另外,为了使NALU主体中不包括与开始码相冲突的,在编码时,每遇到两个字节连续为0,就插入一个字节的0x03。解码时将0x03去掉。也称为脱壳操作。
编码处理过程:
1. 将VCL层输出的SODB封装成nal_unit, NALU是一个通用封装格式,可以适用于有序字节流方式和IP包交换方式。
2. 针对不同的传送网络(电路交换|包交换),将nal_unit封装成针对不同网络的封装格式(比如把nalu封装成rtp包)。
---------------------------------------------------
处理过程一,VCL数据封装成NALU
---------------------------------------------------
VCL层输出的比特流SODB(String Of Data Bits),到nal_unit之间,经过了以下三步处理:
1.SODB字节对齐处理后封装成RBSP(Raw Byte Sequence Payload)。
2.为防止RBSP的字节流与有序字节流传送方式下的SCP(start_code_prefix_one_3bytes,0x000001)出现字节竞争情形,循环检测RBSP前三个字节,在出现字节竞争时在第三字节前加入emulation_prevention_three_byte(0x03),具体方法:
nal_unit( NumBytesInNALunit ) {
forbidden_zero_bit
nal_ref_idc
nal_unit_type
NumBytesInRBSP = 0
for( i = 1; i < NumBytesInNALunit; i++ ) {
if( i + 2 < NumBytesInNALunit && next_bits( 24 ) = = 0x000003 ) {
rbsp_byte[ NumBytesInRBSP++ ]
rbsp_byte[ NumBytesInRBSP++ ]
i += 2
emulation_prevention_three_byte /* equal to 0x03 */
} else
rbsp_byte[ NumBytesInRBSP++ ]
}
}
3. 防字节竞争处理后的RBSP再加一个字节的header(forbidden_zero_bit+ nal_ref_idc+ nal_unit_type),封装成nal_unit.
------------------------------------------------
处理过程二,NALU的RTP打包
------------------------------------------------
一、NALU打包成RTP的方式有三种:
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.
还记得前面nal_unit_type的定义吧,0~23是给H264用的,24~31未使用,在rtp打包时,如果一个NALU放在一个RTP包里,可 以使用NALU的nal_unit_type,但是当需要把多个NALU打包成一个RTP包,或者需要把一个NALU打包成多个RTP包时,就定义新的 type来标识。
Type Packet Type name
---------------------------------------------------------
0 undefined -
1-23 NAL unit Single NAL unit packet per H.264
24 STAP-A Single-time aggregation packet
25 STAP-B Single-time aggregation packet
26 MTAP16 Multi-time aggregation packet
27 MTAP24 Multi-time aggregation packet
28 FU-A Fragmentation unit
29 FU-B Fragmentation unit
30-31 undefined
二、三种打包方式的具体格式
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 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
如有一个 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 组合封包模式
其次, 当 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 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
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 14. RTP payload format for FU-A
FU indicator有以下格式:
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
FU指示字节的类型域 Type=28表示FU-A。。NRI域的值必须根据分片NAL单元的NRI域的值设置。
FU header的格式如下:
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|S|E|R| Type |
+---------------+
S: 1 bit
当设置成1,开始位指示分片NAL单元的开始。当跟随的FU荷载不是分片NAL单元荷载的开始,开始位设为0。
E: 1 bit
当设置成1, 结束位指示分片NAL单元的结束,即, 荷载的最后字节也是分片NAL单元的最后一个字节。当跟随的FU荷载不是分片NAL单元的最后分片,结束位设置为0。
R: 1 bit
保留位必须设置为0,接收者必须忽略该位。
Type: 5 bits
三、拆包和解包
拆包:当编码器在编码时需要将原有一个NAL按照FU-A进行分片,原有的NAL的单元头与分片后的FU-A的单元头有如下关系:
原始的NAL头的前三位为FU indicator的前三位,原始的NAL头的后五位为FU header的后五位,FU indicator与FU header的剩余位数根据实际情况决定。
解包:当接收端收到FU-A的分片数据,需要将所有的分片包组合还原成原始的NAl包时,FU-A的单元头与还原后的NAL的关系如下:
还原后的NAL头的八位是由FU indicator的前三位加FU header的后五位组成,即:
nal_unit_type = (fu_indicator & 0xe0) | (fu_header & 0x1f)
四、代码实现
从RTP包里面得到H264视频数据的方法:
// 功能:解码RTP H.264视频
// 参数:1.RTP包缓冲地址 2.RTP包数据大小 3.H264输出地址 4.输出数据大小
// 返回:true:表示一帧结束 false:FU-A分片未结束或帧未结束
#define RTP_HEADLEN 12
bool UnpackRTPH264( void * bufIn, int len, void ** pBufOut, int * pOutLen)
{
* pOutLen = 0 ;
if (len < RTP_HEADLEN)
{
return false ;
}
unsigned char * src = (unsigned char * )bufIn + RTP_HEADLEN;
unsigned char head1 = * src; // 获取第一个字节
unsigned char head2 = * (src + 1 ); // 获取第二个字节
unsigned char nal = head1 & 0x1f ; // 获取FU indicator的类型域,
unsigned char flag = head2 & 0xe0 ; // 获取FU header的前三位,判断当前是分包的开始、中间或结束
unsigned char nal_fua = (head1 & 0xe0 ) | (head2 & 0x1f ); // FU_A nal
bool bFinishFrame = false ;
if (nal == 0x1c ) // 判断NAL的类型为0x1c=28,说明是FU-A分片
{ // fu-a
if (flag == 0x80 ) // 开始
{
* pBufOut = src - 3 ;
* (( int * )( * pBufOut)) = 0x01000000 ; // zyf:大模式会有问题
* (( char * )( * pBufOut) + 4 ) = nal_fua;
* pOutLen = len - RTP_HEADLEN + 3 ;
}
else if (flag == 0x40 ) // 结束
{
* pBufOut = src + 2 ;
* pOutLen = len - RTP_HEADLEN - 2 ;
}
else // 中间
{
* pBufOut = src + 2 ;
* pOutLen = len - RTP_HEADLEN - 2 ;
}
}
else // 单包数据
{
* pBufOut = src - 4 ;
* (( int * )( * pBufOut)) = 0x01000000 ; // zyf:大模式会有问题
* pOutLen = len - RTP_HEADLEN + 4 ;
}
unsigned char * bufTmp = (unsigned char * )bufIn;
if (bufTmp[ 1 ] & 0x80 )
{
bFinishFrame = true ; // rtp mark
}
else
{
bFinishFrame = false ;
}
return bFinishFrame;
}
从RTP包里面得到AAC音频数据的方法:
//功能:解RTP AAC音频包,声道和采样频率必须知道。
//参数:1.RTP包缓冲地址 2.RTP包数据大小 3.H264输出地址 4.输出数据大小
//返回:true:表示一帧结束 false:帧未结束 一般AAC音频包比较小,没有分片。
bool UnpackRTPAAC(void * bufIn, int recvLen, void** pBufOut, int* pOutLen)
{
unsigned char* bufRecv = (unsigned char*)bufIn;
//char strFileName[20];
unsigned char ADTS[] = {0xFF, 0xF1, 0x00, 0x00, 0x00, 0x00, 0xFC};
int audioSamprate = 32000;//音频采样率
int audioChannel = 2;//音频声道 1或2
int audioBit = 16;//16位 固定
switch(audioSamprate)
{
case 16000:
ADTS[2] = 0x60;
break;
case 32000:
ADTS[2] = 0x54;
break;
case 44100:
ADTS[2] = 0x50;
break;
case 48000:
ADTS[2] = 0x4C;
break;
case 96000:
ADTS[2] = 0x40;
break;
default:
break;
}
ADTS[3] = (audioChannel==2)?0x80:0x40;
int len = recvLen - 16 + 7;
len <<= 5;//8bit * 2 - 11 = 5(headerSize 11bit)
len |= 0x1F;//5 bit 1
ADTS[4] = len>>8;
ADTS[5] = len & 0xFF;
*pBufOut = (char*)bufIn+16-7;
memcpy(*pBufOut, ADTS, sizeof(ADTS));
*pOutLen = recvLen - 16 + 7;
unsigned char* bufTmp = (unsigned char*)bufIn;
bool bFinishFrame = false;
if (bufTmp[1] & 0x80)
{
//DebugTrace::D("Marker");
bFinishFrame = true;
}
else
{
bFinishFrame = false;
}
return true;
}
编码器输出格式
----------------------------------
总的来说H264的码流的打包方式有两种,一种为annex-b byte stream format的格式,这个是绝大部分编码器的默认输出格式,就是每个帧的开头的3~4个字节是H264的start_code,0x00000001或者0x000001。
另一种是原始的NAL打包格式,就是开始的若干字节(1,2,4字节)是NAL的长度,而不是start_code,此时必须借助某个全局的数据来获得编码器的profile,level,PPS,SPS等信息才可以解码。
@之前还疑惑过PPS 和SPS是哪里来的,答案是编码器给出的。
------------------------------
编码数据流分析
------------------------------
首先明确一下,NAL数据流的组成:开始码+NAL头(forbidden_bit+nal_ref+nal_type)+RBSP
下面对一段H264编码数据流进行分析。
00 00 00 01 67 42 00 1E 99 A0 B1 31 00 00 00 01
H264的数据流分为两种,一种是NAL UNIT stream(RTP),一种是 bits stream,
两者可以互相转换。我们分析的这个是 bit stream,根据AnnexB
00 00 00 01 67 42 00 1E 99 A0 B1 31 是 一个NAL,在两个00 00 00 01之间
0110 0111 0100 0010 0000 0000 0001 1110 1001 1001 1010 0000 1011 0001 0011 0001
forbidden_zero_bit(1)= 0//网络传输正确
nal_ref_idc(2)= 11//参考值为3
nal_unit_type(5) = 0 0111:seq_parameter_set_rbsp( )//7,序列参数集
说明这个NALU的RBSP里装的是SPS数据 ,所以 processSPS
profile_idc(8):42:0100 0010
constraint_set0_flag(1):0
constraint_set1_flag(1):0
constraint_set2_flag(1):0
constraint_set3_flag(1):0
reserved_zero_4bits(4):0
level_idc(8):1E
seq_parameter_set_id(UE(V)):
ue(v): unsigned integer Exp-Golomb-coded syntax element with the left bit first. The parsing process for this descriptor is specified in subclause9.1
uvlC: 1001:根据Table9.1 , value= 0,只占1bit.
根据profile_idc忽略掉一部分。
log2_max_frame_num_minus4(ue(v): 001 1001,len = 5,value= 5
pic_order_cnt_type(ue(v)):01 1010,len = 3,value = 2
根据pic_order_cnt_type忽略几个参数
num_ref_frames(ue):010,len = 3,value = 1
0000 1011 0001 0011 0001
gaps_in_frame_num_value_allowed_flag(1) = 0
pic_width_in_mbs_minus1(ue):000 1011 ,len = 7,value = 10;
pic_height_in_map_units_minus1(ue):0001 001,len = 7,value = 8
frame_mbs_only_flag(1) = 1
忽略1
direct_8x8_inference_flag(1):0
忽略
vui_parameters_present_flag(1):0
忽略
NALU结束
68 CE 38 80 00 00 00 01
0110 1000
forbidden_zero_bit(1)= 0
nal_ref_idc(2)= 11
nal_unit_type(5) =01000:pic_parameter_set_rbsp( ),7.3.2.2//8图像参数集
1100
pic_parameter_set_id (ue)=0
seq_parameter_set_id(ue)=0
entropy_coding_mode_flag(1) :0, 重要的flag,0表示编码Exp-Golomb coded and CAVLC,1表示CABAC
pic_order_present_flag(1):0
1110
num_slice_groups_minus1(ue):0
忽略
num_ref_idx_l0_active_minus1(ue):0
num_ref_idx_l1_active_minus1(ue):0
weighted_pred_flag(1);0
0011 1000 1000 0000
weighted_bipred_idc(2):00
pic_init_qp_minus26 /* relative to 26 */(se):0
pic_init_qs_minus26 /* relative to 26 */(se):0
chroma_qp_index_offset(se):0
deblocking_filter_control_present_flag(1);0
constrained_intra_pred_flag(1):0
redundant_pic_cnt_present_flag(1):0
忽略
NALU结束
65 88 80 21 71 27 1B 88…….3888*16 byte
65:0110 0101
forbidden_zero_bit(1)= 0
nal_ref_idc(2)= 11
nal_unit_type(5) =0 0101:slice_layer_without_partitioning_rbsp( )//IDR帧
Slice
Slice_Header:
first_mb_in_slice(ue):0
slice_type(ue):000 1000 = 7
pic_parameter_set_id(ue) = 0
80 21:000 0000 0010 0001
frame_num(u(v): frame_num is used as an identifier for pictures and shall be represented by log2_max_frame_num_minus4 + 4 bits,9 bits = 0
忽略
if( nal_unit_type = = 5 ) //IDR frame
idr_pic_id(u(e)):0
忽略N多
ref_pic_list_reordering( ) 见7。3。3。1忽略,Islice,SI slice,B slice
nal_ref_idc =11 所以dec_ref_pic_marking( )
nal_unit_type = 5,所以
no_output_of_prior_pics_flag(1):0
long_term_reference_flag(1):0
忽略
。。71 27
001 0111 0001 0010 0111
slice_qp_delta(se(v):001 01,4:-2
忽略
slice_data( ):7.3.4
对I-Slice:忽略N多
进入if( moreDataFlag ) { if( MbaffFrameFlag && ( CurrMbAddr % 2 = = 0 | | ( CurrMbAddr % 2 = = 1 && prevMbSkipped ) ) )mb_field_decoding_flag
macroblock_layer( )}
mb_field_decoding_flag忽略
macroblock_layer( )
mb_type(ue(v):0
mb_pred( mb_type )
prev_intra4x4_pred_mode_flag[ luma4x4BlkIdx ](1bit,对babac是ae(v)):1
1 27:0001 0010 0111
prev_intra4x4_pred_mode_flag[ 1 ] : 0001,0,001
0010 0111
prev_intra4x4_pred_mode_flag[ 2 ] : 0010,0,010
prev_intra4x4_pred_mode_flag[ 3] : 0111,0,111
……16个
1b 88 00 3e cf.
intra_chroma_pred_mode(ue(v)) :最后的一个1bit:0
接下来是macroblock_layer的coded_block_pattern和run level,既系数
c0 06 ad a0 18
1100 0000 0000 0110 1010 0000 0001 1000
coded_block_pattern(me(v):0,根据T= 47,0x2f
mb_qp_delta(se(v):):0 len =1
residual( )见7.3.5.3
residual_block( LumaLevel[ i8x8 * 4 + i4x4 ], 16 )
coeff_token(ce(v): 00 0000 0000 0110 1
nc = 0(left block and top block 相关的):
len: { // 0702
{ 1, 6, 8, 9,10,11,13,13,13,14,14,15,15,16,16,16,16},
{ 0, 2, 6, 8, 9,10,11,13,13,14,14,15,15,15,16,16,16},
{ 0, 0, 3, 7, 8, 9,10,11,13,13,14,14,15,15,16,16,16},
{ 0, 0, 0, 5, 6, 7, 8, 9,10,11,13,14,14,15,15,16,16},
},
{
{ 2, 6, 6, 7, 8, 8, 9,11,11,12,12,12,13,13,13,14,14},
{ 0, 2, 5, 6, 6, 7, 8, 9,11,11,12,12,13,13,14,14,14},
{ 0, 0, 3, 6, 6, 7, 8, 9,11,11,12,12,13,13,13,14,14},
{ 0, 0, 0, 4, 4, 5, 6, 6, 7, 9,11,11,12,13,13,13,14},
},
{
{ 4, 6, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 9,10,10,10,10},
{ 0, 4, 5, 5, 5, 5, 6, 6, 7, 8, 8, 9, 9, 9,10,10,10},
{ 0, 0, 4, 5, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,10,10,10},
{ 0, 0, 0, 4, 4, 4, 4, 4, 5, 6, 7, 8, 8, 9,10,10,10},
},
code:
{ 1, 5, 7, 7, 7, 7,15,11, 8,15,11,15,11,15,11, 7,4},
{ 0, 1, 4, 6, 6, 6, 6,14,10,14,10,14,10, 1,14,10,6},
{ 0, 0, 1, 5, 5, 5, 5, 5,13, 9,13, 9,13, 9,13, 9,5},
{ 0, 0, 0, 3, 3, 4, 4, 4, 4, 4,12,12, 8,12, 8,12,8},
},
{
{ 3,11, 7, 7, 7, 4, 7,15,11,15,11, 8,15,11, 7, 9,7},
{ 0, 2, 7,10, 6, 6, 6, 6,14,10,14,10,14,10,11, 8,6},
{ 0, 0, 3, 9, 5, 5, 5, 5,13, 9,13, 9,13, 9, 6,10,5},
{ 0, 0, 0, 5, 4, 6, 8, 4, 4, 4,12, 8,12,12, 8, 1,4},
},
{
{15,15,11, 8,15,11, 9, 8,15,11,15,11, 8,13, 9, 5,1},
{ 0,14,15,12,10, 8,14,10,14,14,10,14,10, 7,12, 8,4},
{ 0, 0,13,14,11, 9,13, 9,13,10,13, 9,13, 9,11, 7,3},
{ 0, 0, 0,12,11,10, 9, 8,13,12,12,12, 8,12,10, 6,2},
},
根据表查的:
code = 13,len = 15,i= 12,j=2
所以numcoeff = 12,numtrailingones = 2
010 0000 0001 1000: totalzeros:根据numcoeff
int lentab[TOTRUN_NUM][16] =
{
{ 1,3,3,4,4,5,5,6,6,7,7,8,8,9,9,9},
{ 3,3,3,3,3,4,4,4,4,5,5,6,6,6,6},
{ 4,3,3,3,4,4,3,3,4,5,5,6,5,6},
{ 5,3,4,4,3,3,3,4,3,4,5,5,5},
{ 4,4,4,3,3,3,3,3,4,5,4,5},
{ 6,5,3,3,3,3,3,3,4,3,6},
{ 6,5,3,3,3,2,3,4,3,6},
{ 6,4,5,3,2,2,3,3,6},
{ 6,6,4,2,2,3,2,5},
{ 5,5,3,2,2,2,4},
{ 4,4,3,3,1,3},
{ 4,4,2,1,3}, numcoeff开始
{ 3,3,1,2},
{ 2,2,1},
{ 1,1},
};
int codtab[TOTRUN_NUM][16] =
{
{1,3,2,3,2,3,2,3,2,3,2,3,2,3,2,1},
{7,6,5,4,3,5,4,3,2,3,2,3,2,1,0},
{5,7,6,5,4,3,4,3,2,3,2,1,1,0},
{3,7,5,4,6,5,4,3,3,2,2,1,0},
{5,4,3,7,6,5,4,3,2,1,1,0},
{1,1,7,6,5,4,3,2,1,1,0},
{1,1,5,4,3,3,2,1,1,0},
{1,1,1,3,3,2,2,1,0},
{1,0,1,3,2,1,1,1,},
{1,0,1,3,2,1,1,},
{0,1,1,2,1,3},
{0,1,1,1,1}, numcoeff开始
{0,1,1,1},
{0,1,1},
{0,1},
};
Code = 1,len = 2,i=2,j = 0, totzeros = 2
Read run: 0 0000 0001 1000根据totzeros = 2
int lentab[TOTRUN_NUM][16] =
{
{1,1},
{1,2,2},
{2,2,2,2},
{2,2,2,3,3},
{2,2,3,3,3,3},
{2,3,3,3,3,3,3},
{3,3,3,3,3,3,3,4,5,6,7,8,9,10,11},
};
int codtab[TOTRUN_NUM][16] =
{
{1,0},
{1,1,0},
{3,2,1,0},
{3,2,1,1,0},
{3,2,3,2,1,0},
{3,0,1,3,2,5,4},
{7,6,5,4,3,2,1,1,1,1,1,1,1,1,1},
Code = 1,len =1,I = 0,j = 0
0.1.1 Slice data syntax
slice_data( ) { |
C |
Descriptor |
if( entropy_coding_mode_flag ) |
||
while( !byte_aligned( ) ) |
||
cabac_alignment_one_bit |
2 |
f(1) |
CurrMbAddr = first_mb_in_slice * ( 1 + MbaffFrameFlag ) |
||
moreDataFlag = 1 |
||
prevMbSkipped = 0 |
||
do { |
||
if( slice_type != I && slice_type != SI ) |
||
if( !entropy_coding_mode_flag ) { |
||
mb_skip_run |
2 |
ue(v) |
prevMbSkipped = ( mb_skip_run > 0 ) |
||
for( i=0; i<mb_skip_run; i++ ) |
||
CurrMbAddr = NextMbAddress( CurrMbAddr ) |
||
moreDataFlag = more_rbsp_data( ) |
||
} else { |
||
mb_skip_flag |
2 |
ae(v) |
moreDataFlag = !mb_skip_flag |
||
} |
||
if( moreDataFlag ) { |
||
if( MbaffFrameFlag && ( CurrMbAddr % 2 = = 0 | | ( CurrMbAddr % 2 = = 1 && prevMbSkipped ) ) ) |
||
mb_field_decoding_flag |
2 |
u(1) | ae(v) |
macroblock_layer( ) |
2 | 3 | 4 |
|
} |
||
if( !entropy_coding_mode_flag ) |
||
moreDataFlag = more_rbsp_data( ) |
||
else { |
||
if( slice_type != I && slice_type != SI ) |
||
prevMbSkipped = mb_skip_flag |
||
if( MbaffFrameFlag && CurrMbAddr % 2 = = 0 ) |
||
moreDataFlag = 1 |
||
else { |
||
end_of_slice_flag |
2 |
ae(v) |
moreDataFlag = !end_of_slice_flag |
||
} |
||
} |
||
CurrMbAddr = NextMbAddress( CurrMbAddr ) |
||
} while( moreDataFlag ) |
||
} |
se(v) : CABAC正式介绍。根据Table 9 5 – coeff_token mapping to TotalCoeff( coeff_token ) and TrailingOnes( coeff_token )。
chroma_format_idc 无
Table9‑1 – Bit strings with “prefix” and “suffix” bits and assignment to codeNum ranges (informative)
Bit string form |
Range of codeNum |
1 |
0 |
0 1 x0 |
1-2 |
0 0 1 x1 x0 |
3-6 |
0 0 0 1 x2 x1 x0 |
7-14 |
0 0 0 0 1 x3 x2 x1 x0 |
15-30 |
0 0 0 0 0 1 x4 x3 x2 x1 x0 |
31-62 |
… |
… |
0.1.1.1 Slice layer without partitioning RBSP syntax
slice_layer_without_partitioning_rbsp( ) { |
C |
Descriptor |
slice_header( ) |
2 |
|
slice_data( ) /* all categories of slice_data( ) syntax */ |
2 | 3 | 4 |
|
rbsp_slice_trailing_bits( ) |
2 |
|
} |
0.1.1.2 Sequence parameter set RBSP syntax
seq_parameter_set_rbsp( ) { |
C |
Descriptor |
profile_idc |
0 |
u(8) |
constraint_set0_flag |
0 |
u(1) |
constraint_set1_flag |
0 |
u(1) |
constraint_set2_flag |
0 |
u(1) |
constraint_set3_flag |
0 |
u(1) |
reserved_zero_4bits /* equal to 0 */ |
0 |
u(4) |
level_idc |
0 |
u(8) |
seq_parameter_set_id |
0 |
ue(v) |
if( profile_idc = = 100 | | profile_idc = = 110 | | |
||
chroma_format_idc |
0 |
ue(v) |
if( chroma_format_idc = = 3 ) |
||
residual_colour_transform_flag |
0 |
u(1) |
bit_depth_luma_minus8 |
0 |
ue(v) |
bit_depth_chroma_minus8 |
0 |
ue(v) |
qpprime_y_zero_transform_bypass_flag |
0 |
u(1) |
seq_scaling_matrix_present_flag |
0 |
u(1) |
if( seq_scaling_matrix_present_flag ) |
||
for( i = 0; i < 8; i++ ) { |
||
seq_scaling_list_present_flag[ i] |
0 |
u(1) |
if( seq_scaling_list_present_flag[ i ] ) |
||
if( i < 6 ) |
||
scaling_list( ScalingList4x4[ i ], 16, |
0 |
|
else |
||
scaling_list( ScalingList8x8[ i – 6 ], 64, |
0 |
|
} |
||
} |
||
log2_max_frame_num_minus4 |
0 |
ue(v) |
pic_order_cnt_type |
0 |
ue(v) |
if( pic_order_cnt_type = = 0 ) |
||
log2_max_pic_order_cnt_lsb_minus4 |
0 |
ue(v) |
else if( pic_order_cnt_type = = 1 ) { |
||
delta_pic_order_always_zero_flag |
0 |
u(1) |
offset_for_non_ref_pic |
0 |
se(v) |
offset_for_top_to_bottom_field |
0 |
se(v) |
num_ref_frames_in_pic_order_cnt_cycle |
0 |
ue(v) |
for( i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++ ) |
||
offset_for_ref_frame[ i ] |
0 |
se(v) |
} |
||
num_ref_frames |
0 |
ue(v) |
gaps_in_frame_num_value_allowed_flag |
0 |
u(1) |
pic_width_in_mbs_minus1 |
0 |
ue(v) |
pic_height_in_map_units_minus1 |
0 |
ue(v) |
frame_mbs_only_flag |
0 |
u(1) |
if( !frame_mbs_only_flag ) |
||
mb_adaptive_frame_field_flag |
0 |
u(1) |
direct_8x8_inference_flag |
0 |
u(1) |
frame_cropping_flag |
0 |
u(1) |
if( frame_cropping_flag ) { |
||
frame_crop_left_offset |
0 |
ue(v) |
frame_crop_right_offset |
0 |
ue(v) |
frame_crop_top_offset |
0 |
ue(v) |
frame_crop_bottom_offset |
0 |
ue(v) |
} |
||
vui_parameters_present_flag |
0 |
u(1) |
if( vui_parameters_present_flag ) |
||
vui_parameters( ) |
0 |
|
rbsp_trailing_bits( ) |
0 |
|
} |
Table7‑1 – NAL unit type codes
nal_unit_type |
Content of NAL unit and RBSP syntax structure |
C |
0 |
Unspecified |
|
1 |
Coded slice of a non-IDR picture |
2, 3, 4 |
2 |
Coded slice data partition A |
2 |
3 |
Coded slice data partition B |
3 |
4 |
Coded slice data partition C |
4 |
5 |
Coded slice of an IDR picture |
2, 3 |
6 |
Supplemental enhancement information (SEI) |
5 |
7 |
Sequence parameter set |
0 |
8 |
Picture parameter set |
1 |
9 |
Access unit delimiter |
6 |
10 |
End of sequence |
7 |
11 |
End of stream |
8 |
12 |
Filler data |
9 |
13 |
Sequence parameter set extension |
10 |
14..18 |
Reserved |
|
19 |
Coded slice of an auxiliary coded picture without partitioning |
2, 3, 4 |
20..23 |
Reserved |
|
24..31 |
Unspecified |
byte_stream_nal_unit( NumBytesInNALunit ) { |
C |
Descriptor |
while( next_bits( 24 ) != 0x000001 && |
||
leading_zero_8bits /* equal to 0x00 */ |
f(8) |
|
if( next_bits( 24 ) != 0x000001 ) |
||
zero_byte /* equal to 0x00 */ |
f(8) |
|
start_code_prefix_one_3bytes /* equal to 0x000001 */ |
f(24) |
|
nal_unit( NumBytesInNALunit) |
||
while( more_data_in_byte_stream( ) && |
||
trailing_zero_8bits /* equal to 0x00 */ |
f(8) |
|
} |
0.1.1.3 Picture parameter set RBSP syntax
pic_parameter_set_rbsp( ) { |
C |
Descriptor |
pic_parameter_set_id |
1 |
ue(v) |
seq_parameter_set_id |
1 |
ue(v) |
entropy_coding_mode_flag |
1 |
u(1) |
pic_order_present_flag |
1 |
u(1) |
num_slice_groups_minus1 |
1 |
ue(v) |
if( num_slice_groups_minus1 > 0 ) { |
||
slice_group_map_type |
1 |
ue(v) |
if( slice_group_map_type = = 0 ) |
||
for( iGroup = 0; iGroup <= num_slice_groups_minus1; iGroup++ ) |
||
run_length_minus1[ iGroup ] |
1 |
ue(v) |
else if( slice_group_map_type = = 2 ) |
||
for( iGroup = 0; iGroup < num_slice_groups_minus1; iGroup++ ) { |
||
top_left[ iGroup ] |
1 |
ue(v) |
bottom_right[ iGroup ] |
1 |
ue(v) |
} |
||
else if( slice_group_map_type = = 3 | | |
||
slice_group_change_direction_flag |
1 |
u(1) |
slice_group_change_rate_minus1 |
1 |
ue(v) |
} else if( slice_group_map_type = = 6 ) { |
||
pic_size_in_map_units_minus1 |
1 |
ue(v) |
for( i = 0; i <= pic_size_in_map_units_minus1; i++ ) |
||
slice_group_id[ i ] |
1 |
u(v) |
} |
||
} |
||
num_ref_idx_l0_active_minus1 |
1 |
ue(v) |
num_ref_idx_l1_active_minus1 |
1 |
ue(v) |
weighted_pred_flag |
1 |
u(1) |
weighted_bipred_idc |
1 |
u(2) |
pic_init_qp_minus26 /* relative to 26 */ |
1 |
se(v) |
pic_init_qs_minus26 /* relative to 26 */ |
1 |
se(v) |
chroma_qp_index_offset |
1 |
se(v) |
deblocking_filter_control_present_flag |
1 |
u(1) |
constrained_intra_pred_flag |
1 |
u(1) |
redundant_pic_cnt_present_flag |
1 |
u(1) |
if( more_rbsp_data( ) ) { |
||
transform_8x8_mode_flag |
1 |
u(1) |
pic_scaling_matrix_present_flag |
1 |
u(1) |
if( pic_scaling_matrix_present_flag ) |
||
for( i = 0; i < 6 + 2* transform_8x8_mode_flag; i++ ) { |
||
pic_scaling_list_present_flag[ i] |
1 |
u(1) |
if( pic_scaling_list_present_flag[ i ] ) |
||
if( i < 6 ) |
||
scaling_list( ScalingList4x4[ i ], 16, |
1 |
|
else |
||
scaling_list( ScalingList8x8[ i – 6 ], 64, |
1 |
|
} |
||
second_chroma_qp_index_offset |
1 |
se(v) |
} |
||
rbsp_trailing_bits( ) |
1 |
|
} |
0.1.2 Slice header syntax
slice_header( ) { |
C |
Descriptor |
first_mb_in_slice |
2 |
ue(v) |
slice_type |
2 |
ue(v) |
pic_parameter_set_id |
2 |
ue(v) |
frame_num |
2 |
u(v) |
if( !frame_mbs_only_flag ) { |
||
field_pic_flag |
2 |
u(1) |
if( field_pic_flag ) |
||
bottom_field_flag |
2 |
u(1) |
} |
||
if( nal_unit_type = = 5 ) |
||
idr_pic_id |
2 |
ue(v) |
if( pic_order_cnt_type = = 0 ) { |
||
pic_order_cnt_lsb |
2 |
u(v) |
if( pic_order_present_flag && !field_pic_flag ) |
||
delta_pic_order_cnt_bottom |
2 |
se(v) |
} |
||
if( pic_order_cnt_type = = 1 && !delta_pic_order_always_zero_flag ) { |
||
delta_pic_order_cnt[ 0 ] |
2 |
se(v) |
if( pic_order_present_flag && !field_pic_flag ) |
||
delta_pic_order_cnt[ 1 ] |
2 |
se(v) |
} |
||
if( redundant_pic_cnt_present_flag ) |
||
redundant_pic_cnt |
2 |
ue(v) |
if( slice_type = = B ) |
||
direct_spatial_mv_pred_flag |
2 |
u(1) |
if( slice_type = = P | | slice_type = = SP | | slice_type = = B ) { |
||
num_ref_idx_active_override_flag |
2 |
u(1) |
if( num_ref_idx_active_override_flag ) { |
||
num_ref_idx_l0_active_minus1 |
2 |
ue(v) |
if( slice_type = = B ) |
||
num_ref_idx_l1_active_minus1 |
2 |
ue(v) |
} |
||
} |
||
ref_pic_list_reordering( ) |
2 |
|
if( ( weighted_pred_flag && ( slice_type = = P | | slice_type = = SP ) ) | | |
||
pred_weight_table( ) |
2 |
|
if( nal_ref_idc != 0 ) |
||
dec_ref_pic_marking( ) |
2 |
|
if( entropy_coding_mode_flag && slice_type != I && slice_type != SI ) |
||
cabac_init_idc |
2 |
ue(v) |
slice_qp_delta |
2 |
se(v) |
if( slice_type = = SP | | slice_type = = SI ) { |
||
if( slice_type = = SP ) |
||
sp_for_switch_flag |
2 |
u(1) |
slice_qs_delta |
2 |
se(v) |
} |
||
if( deblocking_filter_control_present_flag ) { |
||
disable_deblocking_filter_idc |
2 |
ue(v) |
if( disable_deblocking_filter_idc != 1 ) { |
||
slice_alpha_c0_offset_div2 |
2 |
se(v) |
slice_beta_offset_div2 |
2 |
se(v) |
} |
||
} |
||
if( num_slice_groups_minus1 > 0 && |
||
slice_group_change_cycle |
2 |
u(v) |
} |
0.1.3 Slice data syntax
slice_data( ) { |
C |
Descriptor |
if( entropy_coding_mode_flag ) |
||
while( !byte_aligned( ) ) |
||
cabac_alignment_one_bit |
2 |
f(1) |
CurrMbAddr = first_mb_in_slice * ( 1 + MbaffFrameFlag ) |
||
moreDataFlag = 1 |
||
prevMbSkipped = 0 |
||
do { |
||
if( slice_type != I && slice_type != SI ) |
||
if( !entropy_coding_mode_flag ) { |
||
mb_skip_run |
2 |
ue(v) |
prevMbSkipped = ( mb_skip_run > 0 ) |
||
for( i=0; i<mb_skip_run; i++ ) |
||
CurrMbAddr = NextMbAddress( CurrMbAddr ) |
||
moreDataFlag = more_rbsp_data( ) |
||
} else { |
||
mb_skip_flag |
2 |
ae(v) |
moreDataFlag = !mb_skip_flag |
||
} |
||
if( moreDataFlag ) { |
||
if( MbaffFrameFlag && ( CurrMbAddr % 2 = = 0 | | ( CurrMbAddr % 2 = = 1 && prevMbSkipped ) ) ) |
||
mb_field_decoding_flag |
2 |
u(1) | ae(v) |
macroblock_layer( ) |
2 | 3 | 4 |
|
} |
||
if( !entropy_coding_mode_flag ) |
||
moreDataFlag = more_rbsp_data( ) |
||
else { |
||
if( slice_type != I && slice_type != SI ) |
||
prevMbSkipped = mb_skip_flag |
||
if( MbaffFrameFlag && CurrMbAddr % 2 = = 0 ) |
||
moreDataFlag = 1 |
||
else { |
||
end_of_slice_flag |
2 |
ae(v) |
moreDataFlag = !end_of_slice_flag |
||
} |
||
} |
||
CurrMbAddr = NextMbAddress( CurrMbAddr ) |
||
} while( moreDataFlag ) |
||
} |
The variable MbaffFrameFlag is derived as follows.
MbaffFrameFlag = ( mb_adaptive_frame_field_flag && !field_pic_flag ) (7-22)
0.1.4 Macroblock layer syntax
macroblock_layer( ) { |
C |
Descriptor |
mb_type |
2 |
ue(v) | ae(v) |
if( mb_type = = I_PCM ) { |
||
while( !byte_aligned( ) ) |
||
pcm_alignment_zero_bit |
2 |
f(1) |
for( i = 0; i < 256; i++ ) |
||
pcm_sample_luma[ i ] |
2 |
u(v) |
for( i = 0; i < 2 * MbWidthC * MbHeightC; i++ ) |
||
pcm_sample_chroma[ i ] |
2 |
u(v) |
} else { |
||
noSubMbPartSizeLessThan8x8Flag = 1 |
||
if( mb_type != I_NxN && MbPartPredMode( mb_type, 0 ) != Intra_16x16 && NumMbPart( mb_type ) = = 4 ) { |
||
sub_mb_pred( mb_type ) |
2 |
|
for( mbPartIdx = 0; mbPartIdx < 4; mbPartIdx++ ) |
||
if( sub_mb_type[ mbPartIdx ] != B_Direct_8x8 ) { |
||
if( NumSubMbPart( sub_mb_type[ mbPartIdx ] ) > 1 ) |
||
noSubMbPartSizeLessThan8x8Flag = 0 |
||
} else if( !direct_8x8_inference_flag ) |
||
noSubMbPartSizeLessThan8x8Flag = 0 |
||
} else { |
||
if( transform_8x8_mode_flag && mb_type = = I_NxN ) |
||
transform_size_8x8_flag |
2 |
u(1) | ae(v) |
mb_pred( mb_type ) |
2 |
|
} |
||
if( MbPartPredMode( mb_type, 0 ) != Intra_16x16 ) { |
||
coded_block_pattern |
2 |
me(v) | ae(v) |
if( CodedBlockPatternLuma > 0 && transform_8x8_mode_flag && mb_type != I_NxN && noSubMbPartSizeLessThan8x8Flag && ( mb_type != B_Direct_16x16 | | direct_8x8_inference_flag ) ) |
||
transform_size_8x8_flag |
2 |
u(1) | ae(v) |
} |
||
if( CodedBlockPatternLuma > 0 | | CodedBlockPatternChroma > 0 | | |
||
mb_qp_delta |
2 |
se(v) | ae(v) |
residual( ) |
3 | 4 |
|
} |
||
} |
||
} |
0.1.4.1 Macroblock prediction syntax
mb_pred( mb_type ) { |
C |
Descriptor |
if( MbPartPredMode( mb_type, 0 ) = = Intra_4x4 | | |
||
if( MbPartPredMode( mb_type, 0 ) = = Intra_4x4 ) |
||
for( luma4x4BlkIdx=0; luma4x4BlkIdx<16; luma4x4BlkIdx++ ) { |
||
prev_intra4x4_pred_mode_flag[ luma4x4BlkIdx ] |
2 |
u(1) | ae(v) |
if( !prev_intra4x4_pred_mode_flag[ luma4x4BlkIdx ] ) |
||
rem_intra4x4_pred_mode[ luma4x4BlkIdx ] |
2 |
u(3) | ae(v) |
} |
||
if( MbPartPredMode( mb_type, 0 ) = = Intra_8x8 ) |
||
for( luma8x8BlkIdx=0; luma8x8BlkIdx<4; luma8x8BlkIdx++ ) { |
||
prev_intra8x8_pred_mode_flag[ luma8x8BlkIdx ] |
2 |
u(1) | ae(v) |
if( !prev_intra8x8_pred_mode_flag[ luma8x8BlkIdx ] ) |
||
rem_intra8x8_pred_mode[ luma8x8BlkIdx ] |
2 |
u(3) | ae(v) |
} |
||
if( chroma_format_idc != 0 ) |
||
intra_chroma_pred_mode |
2 |
ue(v) | ae(v) |
} else if( MbPartPredMode( mb_type, 0 ) != Direct ) { |
||
for( mbPartIdx = 0; mbPartIdx < NumMbPart( mb_type ); mbPartIdx++) |
||
if( ( num_ref_idx_l0_active_minus1 > 0 | | |
||
ref_idx_l0[ mbPartIdx ] |
2 |
te(v) | ae(v) |
for( mbPartIdx = 0; mbPartIdx < NumMbPart( mb_type ); mbPartIdx++) |
||
if( ( num_ref_idx_l1_active_minus1 > 0 | | |
||
ref_idx_l1[ mbPartIdx ] |
2 |
te(v) | ae(v) |
for( mbPartIdx = 0; mbPartIdx < NumMbPart( mb_type ); mbPartIdx++) |
||
if( MbPartPredMode ( mb_type, mbPartIdx ) != Pred_L1 ) |
||
for( compIdx = 0; compIdx < 2; compIdx++ ) |
||
mvd_l0[ mbPartIdx ][ 0 ][ compIdx] |
2 |
se(v) | ae(v) |
for( mbPartIdx = 0; mbPartIdx < NumMbPart( mb_type ); mbPartIdx++) |
||
if( MbPartPredMode( mb_type, mbPartIdx ) != Pred_L0 ) |
||
for( compIdx = 0; compIdx < 2; compIdx++ ) |
||
mvd_l1[ mbPartIdx ][ 0 ][ compIdx] |
2 |
se(v) | ae(v) |
} |
||
} |
0.1.4.2 Residual data syntax
residual( ) { |
C |
Descriptor |
if( !entropy_coding_mode_flag ) |
||
residual_block = residual_block_cavlc |
||
else |
||
residual_block = residual_block_cabac |
||
if( MbPartPredMode( mb_type, 0 ) = = Intra_16x16 ) |
||
residual_block( Intra16x16DCLevel, 16 ) |
3 |
|
for( i8x8 = 0; i8x8 < 4; i8x8++ ) /* each luma 8x8 block */ |
||
if( !transform_size_8x8_flag | | !entropy_coding_mode_flag ) |
||
for( i4x4 = 0; i4x4 < 4; i4x4++ ) { /* each 4x4 sub-block of block */ |
||
if( CodedBlockPatternLuma & ( 1 << i8x8 ) ) |
||
if( MbPartPredMode( mb_type, 0 ) = = Intra_16x16 ) |
||
residual_block( Intra16x16ACLevel[ i8x8 * 4 + i4x4 ], 15 ) |
3 |
|
else |
||
residual_block( LumaLevel[ i8x8 * 4 + i4x4 ], 16 ) |
3 | 4 |
|
else if( MbPartPredMode( mb_type, 0 ) = = Intra_16x16 ) |
||
for( i = 0; i < 15; i++ ) |
||
Intra16x16ACLevel[ i8x8 * 4 + i4x4 ][ i ] = 0 |
||
else |
||
for( i = 0; i < 16; i++ ) |
||
LumaLevel[ i8x8 * 4 + i4x4 ][ i ] = 0 |
||
if( !entropy_coding_mode_flag && transform_size_8x8_flag ) |
||
for( i = 0; i < 16; i++ ) |
||
LumaLevel8x8[ i8x8 ][ 4 * i + i4x4 ] = |
||
} |
||
else if( CodedBlockPatternLuma & ( 1 << i8x8 ) ) |
||
residual_block( LumaLevel8x8[ i8x8 ], 64 ) |
3 | 4 |
|
else |
||
for( i = 0; i < 64; i++ ) |
||
LumaLevel8x8[ i8x8 ][ i ] = 0 |
||
if( chroma_format_idc != 0 ) { |
||
NumC8x8 = 4 / ( SubWidthC * SubHeightC ) |
||
for( iCbCr = 0; iCbCr < 2; iCbCr++ ) |
||
if( CodedBlockPatternChroma & 3 ) /* chroma DC residual present */ |
||
residual_block( ChromaDCLevel[ iCbCr ], 4 * NumC8x8 ) |
3 | 4 |
|
else |
||
for( i = 0; i < 4 * NumC8x8; i++ ) |
||
ChromaDCLevel[ iCbCr ][ i ] = 0 |
||
for( iCbCr = 0; iCbCr < 2; iCbCr++ ) |
||
for( i8x8 = 0; i8x8 < NumC8x8; i8x8++ ) |
||
for( i4x4 = 0; i4x4 < 4; i4x4++ ) |
||
if( CodedBlockPatternChroma & 2 ) |
||
residual_block( ChromaACLevel[ iCbCr ][ i8x8*4+i4x4 ], 15) |
3 | 4 |
|
else |
||
for( i = 0; i < 15; i++ ) |
||
ChromaACLevel[ iCbCr ][ i8x8*4+i4x4 ][ i ] = 0 |
||
} |
residual_block_cavlc( coeffLevel, maxNumCoeff ) { |
C |
Descriptor |
for( i = 0; i < maxNumCoeff; i++ ) |
||
coeffLevel[ i ] = 0 |
||
coeff_token |
3 | 4 |
ce(v) |
if( TotalCoeff( coeff_token ) > 0 ) { |
||
if( TotalCoeff( coeff_token ) > 10 && TrailingOnes( coeff_token ) < 3 ) |
||
suffixLength = 1 |
||
else |
||
suffixLength = 0 |
||
for( i = 0; i < TotalCoeff( coeff_token ); i++ ) |
||
if( i < TrailingOnes( coeff_token ) ) { |
||
trailing_ones_sign_flag |
3 | 4 |
u(1) |
level[ i ] = 1 – 2 * trailing_ones_sign_flag |
||
} else { |
||
level_prefix |
3 | 4 |
ce(v) |
levelCode = ( Min( 15, level_prefix ) << suffixLength ) |
||
if( suffixLength > 0 | | level_prefix >= 14 ) { |
||
level_suffix |
3 | 4 |
u(v) |
levelCode += level_suffix |
||
} |
||
if( level_prefix > = 15 && suffixLength = = 0 ) |
||
levelCode += 15 |
||
if( level_prefix > = 16 ) |
||
levelCode += ( 1 << ( level_prefix – 3 ) ) – 4096 |
||
if( i = = TrailingOnes( coeff_token ) && |
||
levelCode += 2 |
||
if( levelCode % 2 = = 0 ) |
||
level[ i ] = ( levelCode + 2 ) >> 1 |
||
else |
||
level[ i ] = ( –levelCode – 1 ) >> 1 |
||
if( suffixLength = = 0 ) |
||
suffixLength = 1 |
||
if( Abs( level[ i ] ) > ( 3 << ( suffixLength – 1 ) ) && |
||
suffixLength++ |
||
} |
||
if( TotalCoeff( coeff_token ) < maxNumCoeff ) { |
||
total_zeros |
3 | 4 |
ce(v) |
zerosLeft = total_zeros |
||
} else |
||
zerosLeft = 0 |
||
for( i = 0; i < TotalCoeff( coeff_token ) – 1; i++ ) { |
||
if( zerosLeft > 0 ) { |
||
run_before |
3 | 4 |
ce(v) |
run[ i ] = run_before |
||
} else |
||
run[ i ] = 0 |
||
zerosLeft = zerosLeft – run[ i ] |
||
} |
||
run[ TotalCoeff( coeff_token ) – 1 ] = zerosLeft |
||
coeffNum = ‑1 |
||
for( i = TotalCoeff( coeff_token ) – 1; i >= 0; i-- ) { |
||
coeffNum += run[ i ] + 1 |
||
coeffLevel[ coeffNum ] = level[ i ] |
||
} |
||
} |
||
} |
解码器在解码时,首先逐个字节读取NAL的数据,统计NAL的长度,然后再开始解码。
nal_unit( NumBytesInNALunit ) {/* NumBytesInNALunit为统计出来的数据长度 */
forbidden_zero_bit // forbidden_zero_bit 等于 0表示网络传输没有出错
nal_ref_idc // 指示当前 NAL 的优先级。取值范围为 0-3, 值越高,表示当前 NAL 越重要,需要优先受到保护。H.264 规定如果当前 NAL 是属于参考帧的片,或是序列参数集,或是图像参数集这些重要的数据单位时,本句法元素必须大于 0。
nal_unit_type // NAL类型 指明当前 NAL unit 的类型
NumBytesInRBSP = 0
/* rbsp_byte[i] RBSP 的第 i 个字节。 RBSP 指原始字节载荷,它是 NAL 单元的数据部分的封装格式,封装的数据来自 SODB(原始数据比特流)。SODB 是编码后的原始数据,SODB 经封装为 RBSP 后放入 NAL 的数据部分。下面介绍一个 RBSP 的生成顺序。
从 SODB 到 RBSP 的生成过程:
- 如果 SODB 内容是空的,生成的 RBSP 也是空的
- 否则,RBSP 由如下的方式生成:
1) RBSP 的第一个字节直接取自 SODB 的第 1 到 8 个比特,(RBSP 字节内的比特按照从左到右对应为从高到低的顺序排列,most significant),以此类推,RBSP 其余的每个字节都直接取自 SODB的相应比特。RBSP 的最后一个字节包含 SODB 的最后几个比特,及如下的 rbsp_trailing_bits()
2) rbsp_trailing_bits()的第一个比特是 1,接下来填充 0,直到字节对齐。(填充 0 的目的也是为了字节对齐)
3) 最后添加若干个 cabac_zero_word(其值等于 0x0000)
*/
for( i = 1; i <</span> NumBytesInNALunit; i++ ) {
if( i + 2 <</span> NumBytesInNALunit&& next_bits(24 )= = 0x000003 ) {
/* 0x000003伪起始码,需要删除0x03这个字节 */
rbsp_byte[ NumBytesInRBSP++ ]
rbsp_byte[ NumBytesInRBSP++ ]
i += 2 /* 取出前两个0x00后,跳过0x03 */
//emulation_prevention_three_byte NAL 内部为防止与起始码竞争而引入的填充字节 ,值为 0x03。
emulation_prevention_three_byte
} else
rbsp_byte[ NumBytesInRBSP++ ] /* 继续读取后面的字节 */
}
}
序列参数集(SPS)
句法 |
C |
Desc |
seq_parameter_set_rbsp(){ |
||
profile_idc/*指明所用的Profile */ |
0 |
u(8) |
constraint_set0_flag |
0 |
u(1) |
constraint_set1_flag |
0 |
u(1) |
constraint_set1_flag |
0 |
u(1) |
reserved_zero_5bits /* equal to 0 */ |
0 |
u(5) |
level_idc /* 指明所用的Level */ |
0 |
u(8) |
seq_parameter_set_id /*指明本序列参数集的id号,0-31,被图像集引用,编码需要产生新的序列集时,使用新的id,而不是改变原来参数集的内容 */ |
0 |
ue(v) |
log2_max_frame_num_minus4/*为读取元素frame_num服务,frame_num标识图像的解码顺序,frame_num的解码函数是ue(v),其中v=log2_max_frame_num_minus4+4,该元素同时指明frame_num的最大值MaxFrameNum=2( log2_max_frame_num_minus4+4)*/ |
0 |
ue(v) |
pic_order_cnt_type /* 指明poc的编码方法,poc标识图像的播放顺序,poc可以由frame_num计算,也可以显示传送。poc共三种计算方式 */ |
0 |
ue(v) |
if(pic_order_cnt_type==0) |
||
log2_max_pic_order_cnt_lsb_minus4 /* 指明变量MaxPicOrderCntLsb的值,MaxPicOrderCntLsb=2(log2_max_pic_order_cnt_lsb_minus4+4) */ |
0 |
ue(v) |
else if(pic_order_cnt_type==1){ |
||
delta_pic_order_always_zero_flag /* 等于1时,元素delta_pic_order_cnt[0]和delta_pic_order_cnt[1]不在片头中出现,并且它们的默认值是0,等于0时,上述两元素出现的片头中 */ |
0 |
u(1) |
offset_for_non_ref_pic /* 用来计算非参考帧或场的poc,[-231,231-1] */ |
0 |
se(v) |
offset_for_top_to_bottom_field/*计算帧的底场的poc */ |
0 |
se(v) |
num_ref_frames_inpic_order_cnt_cycle /* 用来解码poc,[0.255] */ |
0 |
ue(v) |
for(i=0;i<num_ref_frames_inpic_order_cnt_cycle;i++) |
||
offset_for_ref_frame[i]/*用来解码poc,对于循环中的每个元素指定一个偏移 */ |
0 |
se(v) |
} |
||
num_ref_frames /* 参考帧队列可达到的最大长度,[0,16] */ |
0 |
ue(v) |
gaps_in_frame_num_value_allowed_flag /* 为1,允许slice header中的frame_num不连续 */ |
0 |
u(1) |
pic_width_inmbs_minus1 /*本元素加1,指明以宏块为单位的图像宽度PicWidthInMbs=pic_width_in_mbs_minus1+1 */ |
0 |
ue(v) |
pic_height_in_map_units_minus1 /* 本元素加1,指明以宏块为单位的图像高宽度PicHeightInMapUnitsMbs=pic_height_in_map_units_minus1+1 */ |
0 |
ue(v) |
frame_mbs_only_flag /* 等于0表示本序列中所有图像均为帧编码;等于1,表示可能是帧,也可能场或帧场自适应,具体编码方式由其它元素决定。结合前一元素:FrameHeightInMbs=(2-frame_mbs_only_flag)*PicHeightInMapUnits */ |
0 |
ue(v) |
if(frame_mbs_only_flag) |
||
mb_adaptiv_frame_field_flag /* 指明本序列是否是帧场自适应模式: frame_mbs_only_flag=1,全部是帧 frame_mbs_only_flag=0, mb_adaptiv_frame_field_flag=0,帧场共存 frame_mbs_only_flag=0, mb_adaptiv_frame_field_flag=1,帧场自适应和场共存*/ |
0 |
u(1) |
direct_8x8_inference_flag /* 用于指明B片的直接和skip模式下的运动矢量的计算方式 */ |
0 |
u(1) |
frame_cropping_flag /*解码器是否要将图像裁剪后输出,如果是,后面为裁剪的左右上下的宽度 */ |
0 |
u(1) |
if(frame_cropping_flag){ |
||
frame_crop_left_offset |
0 |
ue(1) |
frame_crop_right_offset |
0 |
ue(1) |
frame_crop_top_offset |
0 |
ue(1) |
frame_crop_bottom_offset |
0 |
ue(1) |
} |
||
vui_parameters_present_flag /* 指明vui子结构是否出现在码流中,vui子结构在附录中指明,用于表征视频格式的信息 */ |
0 |
u(1) |
if(vui_parameters_present_flag) |
||
vui_parameters() |
0 |
|
rbsp_trailing_bits() |
0 |
|
} |
h264的更多相关文章
- Intel Media SDK H264 encoder GOP setting
1 I帧,P帧,B帧,IDR帧,NAL单元 I frame:帧内编码帧,又称intra picture,I 帧通常是每个 GOP(MPEG 所使用的一种视频压缩技术)的第一个帧,经过适度地压缩,做为随 ...
- 解密H264、AAC硬件解码的关键扩展数据处理
通过上一篇文章,我们用ffmpeg分离出一个多媒体容器中的音视频数据,但是很可能这些数据是不能被正确解码的.为什么呢?因为在解码这些数据之前,需要对解码器做一些配置,典型的就是目前流行的高清编码“黄金 ...
- 海思h264解码库
海思的dll,解码h264 解码后转出yuv12 dll自己百度下载 hi_h264dec.dll hi_h264dec_w.dll 调用方法: if (H264Dec.Hi264DecA ...
- 海思H264解码库 hi_h264dec_w.dll 水印问题
上一篇 海思h264解码库 , 实现了H264帧的简单解码,但更换相机后,出现了解码视频中央出现水印的问题,水印如下图 查找网络,基本就这一篇相关的,还没给出好的解决办法. http://bbs. ...
- 2016-03-04记录 H264.TXT 转成 H264.h264
H264.TXT文件 来源于板子上串口输出的数据,需要把该数据转成 *.h264用 H264的软件打开观察 txt中数据截图如下: MATLAB读入数据的代码: clc;close all;clear ...
- iOS8系统H264视频硬件编解码说明
公司项目原因,接触了一下视频流H264的编解码知识,之前项目使用的是FFMpeg多媒体库,利用CPU做视频的编码和解码,俗称为软编软解.该方法比较通用,但是占用CPU资源,编解码效率不高.一般系统都会 ...
- H264编码原理以及I帧、B和P帧详解
H264是新一代的编码标准,以高压缩高质量和支持多种网络的流媒体传输著称,在编码方面,我理解的他的理论依据是:参照一段时间内图像的统计结果表明,在相邻几幅图像画面中,一般有差别的像素只有10%以内的点 ...
- 树莓派摄像头模块转成H264编码通过RTMP实现Html输出
官方原帖 http://www.raspberrypi.org/phpBB3/viewtopic.php?f=43&t=45368&sid=b81f6551e478f0f6e172aa ...
- 从H264码流中获取视频宽高 (SPS帧) 升级篇
之前写过 <从H264码流中获取视频宽高 (SPS帧)> . 但发现很多局限性,而且有时解出来是错误的. 所以重新去研究了. 用了 官方提供的代码库来解析. 花了点时间,从代码库里单独把解 ...
- 从H264码流中获取视频宽高 (SPS帧)
获取.h264视频宽高的方法 花了2个通宵终于搞定.(后面附上完整代码) http://write.blog.csdn.net/postedit/7852406 图像的高和宽在H264的SPS帧中.在 ...
随机推荐
- hdu 2528 Area
2014-07-30 http://acm.hdu.edu.cn/showproblem.php?pid=2528解题思路: 求多边形被一条直线分成两部分的面积分别是多少.因为题目给的直线一定能把多边 ...
- yii中sphinx索引配置解析
#MySQL数据源配置,详情请查看:http://www.coreseek.cn/products-install/mysql/#请先将var/test/documents.sql导入数据库,并配置好 ...
- ArcGIS Engine中加载数据
ArcGIS Engine中加载数据 http://blog.csdn.net/gisstar/article/details/4206822 分类: AE开发积累2009-05-21 16:49 ...
- paper 47 :Latex中文显示(转)
中文支持需要cjk-latex,总得来说中文可以使用GB和GBK两种字体,GBK需要从windows下copy *.ttc或*.ttf, GB字体则在linux下就用. 先说支持GB的中文显示,安装以 ...
- mongodb子文档查询
--子文档分页 -- 测试数据 db.childTests.insert({ "_id" : 1, "item" : "ABC", &quo ...
- c 语言 结构体
一:结构体定义结构体类型变量 三种方式1st:先声明结构体类型,再定义该类型的变量struct student liming,zhangle;2nd:声明类型的同时定义变量struct student ...
- 创建DirectInput接口对象说明---(void **)&m_pDI
读别人代码时遇到的,起初没明白过来这是怎么回事,后来忽然想明白了. if (FAILED(DirectInput8Create(appInstance, DIRECTINPUT_VERSION, II ...
- [转][Automation]- C# SendKey代码表
使用 SendKeys 将键击和组合键击发送到活动应用程序.此类无法实例化.若要发送一个键击给某个类并立即继续程序流,请使用 Send.若要等待键击启动的任何进程,请使用 SendWait. 每个键都 ...
- UIViewController启动过程
流程:判断是否view属性为nil,如果为空,调用loadView方法,如果重写了loadView方法,那么从代码创建普通视图,如果没有重写并且有storyBoard或者xib文件,那么从storyB ...
- mysql笔记01 MySQL架构与历史、Schema与数据类型优化
MySQL架构与历史 1. MySQL架构推荐参考:http://www.cnblogs.com/baochuan/archive/2012/03/15/2397536.html 2. MySQL会解 ...