免费发布信息不收费的网站,网站建设预览,网站正在建设mp4,企业网站系统有哪些系列文章#xff1a;
1、音视频之视频压缩技术及数字视频综述
2、音视频之视频压缩编码的基本原理
3、音视频之H.264/AVC编码器原理
4、音视频之H.264的句法和语义 在编码器输出的码流中#xff0c;数据的基本单位是句法元素。每个句法元素由若干比特组成#xff0c;它表…系列文章
1、音视频之视频压缩技术及数字视频综述
2、音视频之视频压缩编码的基本原理
3、音视频之H.264/AVC编码器原理
4、音视频之H.264的句法和语义 在编码器输出的码流中数据的基本单位是句法元素。每个句法元素由若干比特组成它表示某个特定的物理意义例如宏块类型、量化参数等。句法表征句法元素的组织结构语义阐述句法元素的具体含义。所有的视频编码标准都是通过定义句法和语义来规范编解码器的工作流程。 一、语句 1、语句元素的分层结构 编码器输出的比特码流中每个比特都隶属某个句法元素也就是说码流是由一个个句法元素依次衔接组成的码流中除了句法元素外并不存在专门用于控制或同步的内容。在H.264定义的码流中句法元素被组织成有层次的结构分别描述各个层次的信息。图6.1表现 了这种结构。 句法元素的分层结构有助于更有效地节省码流。例如在一个图像中经常会在各个片之间有相同的数据如果每个片都同时携带这些数据势必会造成码流的浪费。更为有效的做法是将该图像的公共信息抽取出来形成图像一级的句法元素而在片级只携带该片自身独有的句法元素。在H.264中句法元素被组织成序列、图像、片、宏 块、子宏块5个层次。 H.264的分层结构是经过精心设计的与以往的视频编码标准相比有很大的改进这些改进主要针对传输中的错误掩藏在有误码发生时可以提高图像重建的性能。在以往的标准中分层的组织结构如图 6.2所示它们如同TCP/IP的结构每一层都有头部然后在每层的数据部分包含该层的数据。 在这样的结构中每一层的头部和它的数据部分形成管理与被管理的强依赖关系头部的句法元素是该层数据的核心而一旦头部丢失数据部分的信息几乎不可能再被正确地解码出来。尤其在序列层及图像层由于网络中MTU最大传输单元大小的限制不可能将整个层的句法元素全部放入同一个分组中这个时候如果头部所在的分组丢失该层其他分组即使能被正确接收也无法解码造成资源浪费。 在H.264中分层结构最大的不同是取消了序列层和图像层并将原本属于序列和图像头部的大部分句法元素游离出来形成序列和图像两级参数集其余的部分则放入片层。参数集是一个独立的数据单位不依赖于参数集外的其他句法元素。图6.3描述了参数集与参数集外句法元素的关系在图中我们可以看到参数集只是在片层句法元素需要的时候被引用而且一个参数集并不对应某个特定的图像或序列同一个序列参数集可以被多个序列中的图像参数集引用。同理同一个图像参数集也可以被多个图像引用。只在编码器认为需要更新参数集的内容时才会发送出新的参数集。在这种机制下由于参数集是独立的它可以被多次重发或者采用特殊技术加以保护。 在图6.3的描述中参数集与参数集外部的句法元素处于不同信道中这是H.264的一个建议我们可以使用更安全但成本更昂贵的通道来传输参数集而使用成本低但不够可靠的信道传输其他句法元素 只需要保证片层中的某个句法元素需要引用某个参数集时那个参数集已经到达解码器即可也就是参数集在时间上必须先被传送。当然在条件不允许的情况下我们也可以采用妥协的办法在同一个物理信道中传输所有的句法元素但专门为参数集采用安全可靠的通信协议如TCP。当然H.264也允许我们为包括参数集在内的所有句法元素指定同样的通信协议但这时所有参数集必须被多次重发以保证解码器最终至少能接收到一个。在参数集和片使用同个物理信道的情况下图6.3中的信道1和信道2应该被理解为逻辑上的信道因为从逻辑上看参数集与其他句法元素还是处于各自彼此独立的信道中。 H.264在片层增加了新的句法元素指明所引用的参数集的编号同时因为取消了图像层片成为了信道2中最上层的独立的数据单位每个片必须自己携带关于所属图像的编号、大小等基本信息这些信息在同一图像的每个片中都必须是一致的。在编码时H.264的规范要求将参数集、片这些独立的数据单位尽可能各自完整地放入一个分组中进行传送。 从表面上看来H.264关于参数集和片层的结构增加了编码后数据的冗余度比如参数集必须多次重发又如每个片都必须携带一部分相同的关于整个图像的信息而这些数据完全是重复的降低了编码效率但这些技术的采用使得通信的鲁棒性大大增强当数据传输中出现丢包时能够将错误限制在最小范围防止错误的扩散解码后对错误的掩藏和恢复也能起到很好的作用。一个片的丢失将不会影响其他片的解码还可以通过该片前后的片来恢复该片的数据。 H.264片层以下的句法元素的结构大体上和以往标准类似但在相当多的细节上有所改进所有改进的目的不外乎两个在错误发生时防止错误扩散和减少冗余信息提高编码效率。这两者往往是矛盾的。 图6.3所示的码流的结构是一种简化的模型这个模型已经能够正确工作但还不够完善不适合复杂的场合。在复杂的通信环境中 除了片和参数集外还需要其他的数据单位来提供额外的信息。图6.4描述了在复杂通信中的码流中可能出现的数据单位。如前文所述参数集可以被抽取出来使用其他信道传输。 在图6.4中我们看到一个序列的第一个图像叫做IDR图像即时解码刷新图像IDR图像都是I图像。H.264引入IDR图像是为了解码的重同步当解码器解码到IDR图像时立即将参考帧队列清空将已解码的数据全部输出或抛弃重新查找参数集开始一个新的序列。 这样如果在前一个序列的传输中发生重大错误如严重的丢包或其他原因引起数据错位在这里可以获得重新同步。IDR图像之后的图像永远不会引用IDR图像之前的图像的数据来解码。 要注意IDR图像和I图像的区别IDR图像一定是I图像但I图像不一定是IDR图像。一个序列中可以有很多的I图像I图像之后的图像可以引用I图像之间的图像做运动参考。 在图6.4中除了参数集与片外还有其他的数据单位这些数据单位可以提供额外的数据或同步信息这些数据单位也是一系列句法元素的集合。它们在解码过程中不是必需的但却可以适当提高同步性能或定义图像的复杂特征。 2、句法的表示方法 句法元素与变量 编码器将数据编码为句法元素然后依次发送。在解码器端通常要将句法元素作求值计算得出一些中间数据这些中间数据就是H.264定义的变量如图6.5所示。 在图6.5中pic_width_in_mbs_minus1是解码器直接从码流中提取的句法元素这个句法元素表征图像的宽度以宏块为单位。我们看到为了提高编码效率H.264将图像实际的宽度减去1后再传送。 PicWidthInMbspic_width_in_mbs_minus11 PicWidthInSamplesLPicWidthInMbs×16 PicWidthInSamplesCPicWidthInMbs×8 以上变量PicWidthInMbs表示图像以宏块为单位的宽变量 PicWidthInSamplesL、PicWidthInSamplesC分别表示图像的亮度、色度分量以像素为单位的宽。H.264定义这些变量是因为在后续句法元素的提取算法或图像的重建中需要用到它们的值。在H.264中句法元素的名称是由小写字母和一系列的下划线组成而变量名称由大小写字母组成中间没有下划线。 语法 句法是句法元素的组织结构而对一个结构的描述必然少不了对应的语法语法提供判断、循环等必要的描述方法。H.264采用一种类C语法。 描述子 描述子是指从比特流提取句法元素的方法即句法元素的解码算法每个句法元素都有相对应的描述子。由于H.264编码的最后一步是熵编码所以这里的描述子大多是熵编码的解码算法。H.264定义了如下几种描述子 ae(v) 基于上下文自适应的二进制算术熵编码 b(8) 读进连续的8个比特 ce(v) 基于上下文自适应的可变长熵编码 f(n) 读进连续的n个比特 i(n)/i(v) 读进连续的若干比特并把它们解释为有符号整数 me(v) 映射指数Golomb熵编码 se(v) 有符号指数Golomb熵编码 te(v) 截断指数Golomb熵编码 u(n)/u(v) 读进连续的若干比特并将它们解释为无符号整数 ue(v) 无符号指数Golomb熵编码。 我们看到描述子都在括号中带有一个参数这个参数表示需要提取的比特数。当参数是n时表明调用这个描述子的时候会指明n的值也即该句法元素是定长编码的。当参数是v时对应的句法元素是变长编码这时有两种情况i(v)和u(v)两个描述子的v由以前的句法元素指定也就是说在前面会有句法元素指定当前句法元素的比特长度除了这两个描述子外其他描述子都是熵编码它们的解码算法本身能够确定当前句法元素的比特长度。 二、句法表 句法表定义了H.264的句法指明在码流中依次出现的句法元素及它们出现的条件、提取描述子等。就像前文所提句法表是分层嵌套的。H.264的句法表见表6.1至表6.19。 表6.1 NAL层句法 表6.2 序列参数集层句 表6.3 图像参数集层句法 表6.4 片层句法不分区 表6.5 片层A分区句法 表6.6 片层B分区句法 表6.7 片层C分区句法 表6.8 拖尾trailing bits句法 表6.9 片头句法 表6.10 参考帧队列重排序reordering句法 表6.11 加权预测句法 表6.12 参考帧队列标记marking句法 表6.13 片层数据句法 表6.14 宏块层句法 表6.15 宏块层预测句法 表6.16 子宏块预测句法 表6.17 残差句法 表6.18 CAVLC残差句法 表6.19 CABAC残差句法 在句法表中的C字段表示该句法元素的分类这是为片分区服务的句法元素分类的具体含义在表6.20中详细介绍。其中Descriptor 指定对应句法元素的描述子。
表6.20 nal_uint_type语义 三、语义 下面将对句法表中的句法元素作详细解释。 1、NAL层语义 在网络传输的环境下编码器将每个NAL各自独立、完整地放入一个分组由于分组都有头部解码器可以很方便地检测出NAL的分界依次取出NAL进行解码。为了节省码流H.264没有另外在NAL的头部设立表示起始的句法元素我们从表6.1可以看到这点。但是如果编码数据是储存在介质如DVD光盘上由于NAL是依次紧密排列的解码器将无法在数据流中分辨出每个NAL的起始位置和终止位置所以必须要有另外的机制来解决这个问题。 针对这个问题H.264草案的附录B中指明了一种简单又高效的方案。当数据流存储在介质上时在每个NAL前添加起始码0x000001。 在某些类型的介质上为了寻址的方便要求数据流在长度上对 齐或必须是某个常数的倍数。考虑到这种情况H.264建议在起始码前添加若干字节的0来填充直到该NAL的长度符合要求。 在这样的机制下解码器在码流中检测起始码作为一个NAL的起始标识当检测到下一个起始码时当前NAL结束。H.264规定当检测到0x000000时也可以表征当前NAL的结束这是因为连着的3个字节的0中的任何一个字节的0要么属于起始码要么是起始码前面添加的0。 添加起始码是一个解决问题的很好的方法但上面关于起始码的介绍还不完整因为忽略了一个重要的问题如果在NAL内部出现了 0x000001或是0x000000的序列怎么办。毫无疑问这种情况是致命的 解码器将把这些本来不是起始码的字节序列当作起始码而错误地认为这里往后是一个新的NAL的开始进而造成解码数据的错位而我们做的大量实验证明NAL内部经常会出现这样的字节序列。 于是H.264提出了另外一种机制叫做“防止竞争”在编码器编码完一个NAL时应该检测是否出现图6.6左侧中的四个字节序列以防止它们和起始码竞争。如果检测到这些序列存在编码器将在最后一个字节前插入一个新的字节0x03从而使它们变成图6.6右测的样子。当解码器在NAL内部检测到有0x000003的序列时将把0x03抛弃恢复原始数据。 我们可以从句法表6.1中看到解码器在NAL层的处理步骤其中变量NumBytesInNALunit是解码器计算出来的解码器在逐个字节地读一个NAL时并不同时对它解码而是要通过起始码机制将整个NAL读进计算出长度后再开始解码。 forbidden_zero_bit 等于0 nal_ref_idc 指示当前NAL的优先级。取值范围为03值越高 表示当前NAL越重要越需要优先受到保护。H.264规定如果当前NAL是一个序列参数集或一个图像参数集或属于参考图像的片或片分区等重要的数据单位时本句法元素必须大于0。但在大于0时具体该取何值并没有进一步的规定通信双方可以灵活地制定策略。当 nal_unit_type等于5时nal_ref_idc大于0nal_unit_type等于6、9、10、 11或12时nal_ref_idc等于0。 nal_unit_type 指明当前NAL unit的类型具体类型的定义见表 6.20。nal_unit_type5时表示当前NAL是IDR图像的一个片在这种情况下IDR图像中的每个片的nal_unit_type都应该等于5。注意片分区不可用于IDR图像。 rbsp_byte[i] RBSP的第i个字节。RBSP指原始字节载荷它是NAL单元的数据部分的封装格式封装的数据来自SODB原始数据比特流。SODB是编码后的原始数据SODB经封装为RBSP后放入NAL的数据部分。下面介绍一个RBSP的生成顺序。 从SODB到RBSP的生成过程如下所述。 1如果SODB内容是空的生成的RBSP也是空的。 2否则RBSP由如下的方式生成
RBSP的第一个字节直接取自SODB的第18个比特RBSP字节内的比特按照从左到右对应为从高到低的顺序排列以此类推 RBSP其余的每个字节都直接取自SODB的相应比特RBSP的最后一个字节包含SODB的最后几个比特及如下的 rbsp_trailing_bits() rbsp_trailing_bits()的第一个比特是1接下来填充0直到字节对齐 填充0的目的也是为了字节对齐最后添加若干个cabac_zero_word其值等于0x0000。 emulation_prevention_three_byte NAL内部为防止与起始码竞争而引入的填充字节值为0x03。 2、序列参数语义 profile_idc、level_idc 指明所用profile、level。 constraint_set0_flag 等于1时表示必须遵从本章参考文献1中 附录A.2.1所指明的所有制约条件等于0时表示不必遵从所有条件。 constraint_set1_flag 等于1时表示必须遵从本章参考文献1中 附录A.2.2所指明的所有制约条件等于0时表示不必遵从所有条件。 constraint_set2_flag 等于1时表示必须遵从本章参考文献1中 附录A.2.3所指明的所有制约条件等于0时表示不必遵从所有条件。 注意当constraint_set0_flag、constraint_set1_flag、 constraint_set2_flag中有两个以上等于1时本章参考文献1中附录 A.2中的所有制约条件都要被遵从。 reserved_zero_5bits 在目前的标准中本句法元素必须等于0其他的值保留做将来用解码器应该忽略本句法元素的值。 seq_parameter_set_id 指明本序列参数集的id号这个id号将被 picture参数集引用本句法元素的值应该在[0, 31]。 注意当编码器需要产生新的序列参数集时应该使用新的seq_parameter_set_id即使用新的序列参数集而不是去改变原来的参数集中的内容。 log2_max_frame_num_minus4 这个句法元素主要是为读取另一 个句法元素frame_num服务的frame_num是最重要的句法元素之一 它标识所属图像的解码顺序。可以在句法表看到fram-num的解码函数是uev函数中的v在这里指定 vlog2_max_frame_num_minus44 从另一个角度看这个句法元素同时也指明了frame_num的所能达到的最大值 变量MaxFrameNum表示frame_num的最大值。在解码过程中它也是一个非常重要的变量。 值得注意的是frame_num是循环计数的即当它到达MaxFrameNum后又从0重新开始新一轮的计数。解码器必须要有机制检测这种循环不然会引起类似千年虫的问题在图像的顺序上造成 混乱。 pic_order_cnt_type 指明了POCPicture Order Count的编码方法、POC标识图像的播放顺序。由于H.264使用了B帧预测使得图像的解码顺序并不一定等于播放顺序但它们之间存在一定的映射关系。POC可以由frame-num通过映射关系计算得来也可以索性由编码器显式地传送。H.264中一共定义了3种POC的编码方法这个句法元素就是用来通知解码器该用哪种方法来计算POC。pic_order_cnt_type 的取值范围是[0,2]。 在如下的视频序列中本句法元素不应该等于2
一个非参考帧的接入单元后面紧跟着一个非参考图像指参考帧或参考场的接入单元两个分别包含互补非参考场对的接入单元后面紧跟着一个非参考图像的接入单元一个非参考场的接入单元后面紧跟着另外一个非参考场并且这两个场不能构成一个互补场对。 log2_max_pic_order_cnt_lsb_minus4 指明了变量 MaxPicOrderCntLsb的值 该变量在pic_order_cnt_type0时使用。 delta_pic_order_always_zero_flag 等于1时句法元素 delta_pic_order_cnt[0]和delta_pic_order_cnt[1]不在片头出现并且它们 的值默认为0本句法元素等于0时上述的两个句法元素将在片头出现。 offset_for_non_ref_pic 被用来计算非参考帧或场的picture order count本句法元素的值应该在【,】范围内。 offset_for_top_to_bottom_field 被用来计算图像帧中的底场的 picture order count本句法元素的值应该在【,】范围内。 num_ref_frames_in_pic_order_cnt_cycle 被用来解码picture order count本句法元素的值应该在[0,255]范围内。 offset_for_ref___frame[i] 在picture order count type1时用用于解码POC本句法元素对循环num_ref_frames_in_pic_order_cycle中的每一个元素指定一个偏移。 num_ref_frames 指定参考帧队列可能达到的最大长度解码器依照这个句法元素的值开辟存储区这个存储区用于存放已解码的参考帧H.264规定最多可用16个参考帧本句法元素的值最大为16。值得注意的是这个长度以帧为单位如果在场模式下应该相应地扩展 一倍。 gaps_in_frame_num_value_allowed_flag 这个句法元素等于1 时表示允许句法元素frame_num可以不连续。当传输信道堵塞严重 时编码器来不及将编码后的图像全部发出这时允许丢弃若干帧图像。在正常情况下每一帧图像都有依次连续的frame_num值解码器检查到如果frame_num不连续便能确定有图像被编码器丢弃。这时解码器必须启动错误掩藏的机制来近似地恢复这些图像因为这些图像有可能被后续图像用作参考帧。 当这个句法元素等于0时表不允许frame_num不连续即编码器在任何情况下都不能丢弃图像。这时H.264允许解码器可以不去检查 frame_num的连续性以减少计算量。这种情况下如果依然发生frame_num不连续表示在传输中发生丢包解码器会通过其他机制检测到丢包的发生然后启动错误掩藏的恢复图像。 pic_width_in_mbs_minus1 本句法元素加1后指明图像宽度以宏块为单位 PicWidthInMbspic_width_in_mbs_minus11 通过这个句法元素解码器可以计算得到亮度分量以像素为单位的图像宽度 PicWidthInSamplesLPicWidthInMbs×16 从而也可以得到色度分量以像素为单位的图像宽度 PicWidthInSamplesCPicWidthInMbs×8 以上变量PicWidthInSamplesL、PicWidthInSamplesC 分别表示图像 的亮度、色度分量以像素为单位的宽。 H.264将图像的大小在序列参数集中定义意味着可以在通信过程中随着序列参数集动态地改变图像的大小在后文中可以看到甚至可以将传送的图像剪裁后输出。 pic_height_in_map_units_minus1 本句法元素加1后指明图像高度 PicHeightInMapUnitspic_height_in_map_units_minus11 PicSizeInMapUnitsPicWidthInMbs×PicHeightInMapUnits 图像的高度的计算要比宽度的计算复杂因为一个图像可以是帧也可以是场从这个句法元素可以在帧模式和场模式下分别计算出亮度、色度的高。值得注意的是这里以map_unit为单位map_unit的含义由后文叙述。 frame_mbs_only_flag 本句法元素等于1时表示本序列中所有图像的编码模式都是帧没有其他编码模式存在本句法元素等于0时 表示本序列中图像的编码模式可能是帧也可能是场或帧场自适应 某个图像具体是哪一种要由其他句法元素决定。 结合map_unit的含义这里给出上一个句法元素 pic_height_in_map_units_minus1的进一步解析步骤。 当frame_mbs_only_flag等于1pic_height_in_map_units_minus1指的是一个picture中帧的高度当frame_mbs_only_flag等于0pic_heght_in_map_units_minus1指的是一个picture中场的高度所以可以得到如下以宏块为单位的图像高度 FrameHeightInMbs(2 frame_mbs_only_flag)×PicHeightInMapUnits mb_adaptive_frame_field_flag 指明本序列是否属于帧场自适应模式。mb_adaptive_frame_field_flag等于1时表明在本序列中的图像如果不是场模式就是帧场自适应模式等于0时表示本序列中的图像如果不是场模式就是帧模式。 全部是帧对应于frame_mbs_only_flag1的情况。 帧和场共存。frame_mbs_only_flag0, mb_adaptive_frame_field_flag0 帧场自适应和场共存。frame_mbs_only_flag0, mb_adaptive_frame_field_flag1 值得注意的是帧和帧场自适应不能共存在一个序列中。 direct_8x8_inference_flag 用于指明B片的直接和skip模式下运动矢量的预测方法。 frame_cropping_flag 用于指明解码器是否要将图像裁剪后输出如果是的话后面紧跟着的四个句法元素分别指出左、右、上、 下裁剪的宽度。frame_crop_left_offset, frame_crop_right_offset, frame_crop_bottom_offset, frame_crop_bottom_offset 如上一句法元素所述。 vui_parameters_present_flag 指明vui子结构是否出现在码流中 vui的码流结构在本章参考文献1中的附录指明用以表征视频格式等额外信息。 3、图像参数语义 pic_parameter_set_id 用以指定本参数集的序号该序号在各片的片头被引用。 seq_parameter_set_id 指明本图像参数集所引用的序列参数集的序号。 entropy_coding_mode_flag 指明熵编码的选择。本句法元素为0时表示熵编码使用CAVLC本句法元素为1时表示熵编码使用CABAC。 pic_order_present_flag POC的3种计算方法在片层还各需要用一 些句法元素作为参数。本句法元素等于1时表示在片头会有句法元素指明这些参数本句法元素等于0时表示片头不会给出这些参数这些参数使用默认值。 num_slice_groups_minus1 本句法元素加1后指明图像中片组的个数。H.264中没有专门的句法元素用于指明是否使用片组模式当本句法元素等于0时即只有一个片组表示不使用片组模式后面也不会跟有用于计算片组映射的句法元素。 slice_group_map_type 规定了片组的映射类型取值范围为 [0,6]。片组的取值对应的映射类型可参见表5.2。 map_units的定义如下。 1当frame_mbs_only_flag等于1时map_units指的就是宏块。 2当frame_mbs_only_falg等于0时又有以下几种情况
帧场自适应模式时map_units指的是宏块对场模式时map_units指的是宏块帧模式时map_units指的是与帧场自适应帧中的宏块对类似 垂直相邻的两个连续宏块的组合体。 run_length_minus1[i] 用以指明当片组类型等于0时每个片组连续的map_units个数。top_left[i],bottom_right[i] 用以指明当片组类型等于2时矩形区域的左上及右下位置。 slice_group_change_direction_flag 当片组类型等于3、4、5时 本句法元素与下一个句法元素一起指明确切的片组映射类型。 slice_group_change_rate_minus1 用以指明变量 SliceGroupChangeRate。本句法元素表示一个片组的大小从一个图像到下一个的改变的倍数以map_units为单位。 pic_size_in_map_units_minus1 在片组类型等于6时用以指明图像以map_units为单位的大小。 slice_group_id[i] 在片组类型等于6时用以指明某个map_units属于哪个片组。 num_ref_idx_l0_active_minus1 加1后指明目前参考帧队列的长度即有多少个参考帧包括短期和长期。值得注意的是当目前解码图像是场模式下参考帧队列的长度应该是本句法元素再乘以2 因为场模式下各帧必须被分解以场对形式存在这里所说的场模式包括图像的场及帧场自适应下的处于场模式的宏块对。本句法元素的值有可能在片头被重载。 读者可能还记得在序列参数集中有句法元素num_ref_frames也是跟参考帧队列有关它们的区别是num_ref_frames指明参考帧队列的最大值解码器用它的值来分配内存空间num_ref_idx_l0_active_minus1 指明在这个队列中当前实际的、已存在的参考帧的数目这从它的名字“active”中也可以看出来。 这个句法元素是H.264中最重要的句法元素之一。 编码器要通知解码器某个运动矢量所指向的是哪个参考图像时并不是直接传送该图像的编号而是传送该图像在参考帧队列中的序号。这个序号并不是在码流中传送的而是编码器和解码器同步地、用相同的方法将参考图像放入队列从而获得一个序号。这个队列在每解一个图像时甚至是每个片后都会动态地更新。维护参考帧队列是编解码器十分重要的工作而本句法元素是维护参考帧队列的重要依据。参考帧队列的复杂的维护机制也正是H.264重要且很有特色的组成部分。 num_ref_idx_l1_active_minus1 与上一个句法元素的语义一致 只是本句法元素用于list1而上一句法元素用于list0。 weighted_pred_flag 用以指明是否允许P和SP片的加权预测如果允许在片头会出现用以计算加权预测的句法元素。 weighted_bipred_idc 用以指明是否允许B片的加权预测。本句法元素等于0时表示使用默认加权预测模式等于1时表示使用显式加权预测模式等于2时表示使用隐式加权预测模式。 pic_init_qp_minus26 取值范围为[-26,25]加26后用以指明亮度分量的量化参数的初始值。在H.264中量化参数分3个级别给出图像参数集、片头、宏块。在图像参数集给出的是一个初始值。 pic_init_qs_minus26 与上一个句法元素语义一致只是用于SP和SI。 chroma_qp_index_offset 色度分量的量化参数是根据亮度分量的量化参数计算出来的本句法元素用以指明计算时用到的参数。 deblocking_filter_control_present_flag 编码器可以通过句法元素显式地控制去块滤波的强度本句法元素指明是在片头是否会有句法元素传递这个控制信息。如果本句法元素等于0那些用于传递滤波强度的句法元素不会出现解码器将独立地计算出滤波强度。 constrained_intra_pred_flag 在P和B片中帧内编码的宏块的邻近宏块可能是采用的帧间编码。当本句法元素等于1时表示帧内编码的宏块不能用帧间编码的宏块的像素作为自己的预测即帧内编码的宏块只能用邻近帧内编码的宏块的像素作为自己的预测而本句法元素等于0时表示不存在这种限制。 redundant_pic_cnt_present_flag 指明是否会出现 redundant_pic_cnt句法元素。 4、片头语句 first_mb_in_slice 片中的第一个宏块的地址片通过这个句法元素来标定它自己的地址。 要注意的是在帧场自适应模式下宏块都是成对出现的这时本句法元素表示的是第几个宏块对对应的第一个宏块的真实地址应该是2×first_mb_in_slice。 slice_type 指明片的类型具体语义见表6.21。 表6.21 slice_type语义 IDR图像时slice_type等于2、4、7、9。 pic_parameter_set_id 图像参数集的索引号取值范围为 [0,255]。 frame_num 每个参考帧都有一个依次连续的frame_num作为它们的标识这指明了各图像的解码顺序。frame_num所能达到了最大值由前文序列参数集中的句法元素log2_max_frame_num_minus4推出。事实上我们在表中可以看到frame_num的出现没有if语句限定条件这表明非参考帧的片头也会出现frame_num。只是当该个图像是参考帧时 它所携带的这个句法元素在解码时才有意义。见表6.22所示的例子。 H.264对frame_num的值作了如下规定当参数集中的句法元素 gaps_in_frame_num_value_allowed_flag等于0时每个图像的frame_num值是其前一个参考帧的frame_num值加1。这句话包含有两层意思。
当gaps_in_frame_num_value_allowed_flag等于0时即 frame_num连续的情况下每个图像的frame_num由前一个参考帧图像对应的值加1着重点是“前一个参考帧”。在表6.22中第3个图像是B帧按照定义它的frame_nun值应是前一个参考帧即第2个图像对应的值加1即为2第4个图像是P帧由于该序列B帧都不作为参考帧 所以对于该图像来说定义中所谓的“前一个参考帧”仍旧是指的第2个图像所以对于第4个图像来说它的frame_num的取值和第3个图像一样也为2。相同的情况也发生在第6和第8帧上。 前面我们曾经提到对于非参考帧来说它的frame_num值在解码过程中是没有意义的因为frame_num值是参考帧特有的它的主要作 用是在该图像被其他图像引用作运动补偿的参考时提供一个标识。但 H.264并没有在非参考帧图像中取消这一句法元素原因是在POC的第二种和第三种解码方法中可以通过非参考帧的frame_num值计算出它们的POC值。当gaps_in_frame_num_value_allowed_flag等于1前文已经提到这时若网络拥塞编码器可以将编码后的若干图像丢弃而不用另行通知解码器。在这种情况下解码器必须有机制将缺失的frame_num及所对应的图像填补否则后续图像若将运动矢量指向缺失的图像将会产生解码错误。 field_pic_flag 这是在片层标识图像编码模式的唯一一个句法元素。所谓的编码模式是指的帧编码、场编码、帧场自适应编码。当这个句法元素取值为1时属于场编码0时为非场编码。 序列参数集中的句法元素frame_mbs_only_flag和 mb_adaptive_frame_field_flag再加上本句法元素共同决定图像的编码模式如图6.7所示。 在序列参数集中我们已经能够计算出图像的高和宽但需要指出的是这个高是指该序列中图像的帧的高度而一个实际的图像可能是帧也可能是场对于图像的实际高度应进一步作如下处理 PicHeightInMbsFrameHeightInMbs /(1field_pic_flag) 从而我们可以得到在解码器端所用到的其他与图像大小有关的变量。 亮度分量PicHeightInSamplesLPicHeightInMbs×16 色度分量PicHeightInSamplesCPicHeightInMbs×8 PicSizeInMbsPicWidthInMbs×PicHeightInMbs 前文已提到frame_num是参考帧的标识但是在解码器中并不是直接引用的frame_num值而是引用由frame_num进一步计算出来的变量PicNum的值由frame_num映射到PicNum的算法。这里介绍在该算法中用到的两个变量MaxPicNum和CurrPicNum。
MaxPicNum 表征PicNum的最大值。PicNum和frame_num一样也是嵌在循环中当达到这个最大值时PicNum将从0开始重新计数。 如果field_pic_flag0, MaxPicNumMaxFrameNum 否则MaxPicNum2×MaxFrameNum
CurrPicNum 当前图像的PicNum值。在计算PicNum的过程中当前图像的PicNum值是由frame_num直接算出在解某个图像时要将已经解码的各参考帧的PicNum重新计算一遍新的值参考当前图像的PicNum值得来。 如果field_pic_flag0, CurrPicNumframe_num 否则CurrPicNum2×frame_num1 Frame_num是对帧编号的也就是说如果在场模式下同属一个场对的顶场和底场两个图像的frame_num的值是相同的。在帧或帧场自适应模式下就直接将图像的frame_num赋给PicNum而在场模式下将2×frame_num和2×frame_num1两个值分别赋给两个场。 2×frame_num1这个值永远被赋给当前场解码到当前场对的下一个场时刚才被赋为2×frame_num1的场的PicNum值被重新计算为 2×frame_num而将2×frame_num1赋给新的当前场。 bottom_field_flag 等于1时表示当前图像是属于底场等于0时表示当前图像是属于顶场。 idr_pic_id IDR图像的标识。不同的IDR图像有不同的idr_pic_id值。值得注意的是IDR图像不等价于I图像只有在作为IDR图像的I帧才有这个句法元素。在场模式下IDR帧的两个场有相同的idr_pic_id 值。idr_pic_id的取值范围是[0,65535]和frame_num类似当它的值超出这个范围时它会以循环的方式重新开始计数。 pic_order_cnt_lsb 在POC的第一种算法中本句法元素来计算POC值在POC的第一种算法中是显式地传递POC的值而其他两种算法是通过frame_num来映射POC的值。注意这个句法元素的读取函数是 u(v)这个v的值是序列参数集的句法元素 log2_max_pic_order_cnt_lsb_minus4加4个比特而得到。pic_order_cnt_lsb 的取值范围是[0, MaxPicOrderCntLsb -1]。 delta_pic_order_cnt_bottom 如果是在场模式下场对中的两个场都各自被构造为一个图像它们有各自的POC算法来分别计算两个场的POC值也就是一个场对拥有一对POC值而在是帧模式或是帧场自适应模式下一个图像只能根据片头的句法元素计算出一个POC 值。根据H.264的规定在序列中有可能出现场的情况即 frame_mbs_only_flag不为1时每个帧或帧场自适应的图像在解码完后必须分解为两个场以供后续图像中的场作为参考图像。所以当 frame_mb_only_flag不为1时帧或帧场自适应中包含的两个场也必须 有各自的POC值。通过本句法元素可以在已经解开的帧或帧场自适应图像的POC基础上新映射一个POC值并把它赋给底场。当然像句法表指出的那样这个句法元素只用在POC 的第一个算法中。 delta_pic_order_cnt[0]delta_pic_order_cnt[1] 前文已经提到POC的第二和第三种算法是从frame_num映射得来这两个句法元素用于映射算法。delta_pic_order_cnt[0]用于帧编码方式下的底场和场编码方式的场delta_pic_order_cnt[1]用于帧编码方式下的顶场。 redundant_pic_cnt 冗余片的id号取值范围为[0,127]。 direct_spatial_mv_pred_flag 指出在B图像的直接预测的模式下用时间预测还是用空间预测。1表示空间预测0表示时间预测。 num_ref_idx_active_override_flag 在图像参数集中我们看到已经出现句法元素num_ref_idx_l0_active_minus1和 num_ref_idx_l1_active_minus1指定当前参考帧队列中实际可用的参考帧的数目。在片头可以重载这对句法元素以给某特定图像更大的灵活度。这个句法元素就是指明片头是否会重载如果该句法元素等于1 下面会出现新的num_ref_idx_l0_active_minus1和 num_ref_idx_l1_active_minus1值。 num_ref_idx_l0_active_minus1、num_ref_idx_l1_active_minus1 如上个句法元素中所介绍这是重载的num_ref_idx_l0_active_minus1 及num_ref_idx_l1_active_minus1。 cabac_init_idc 给出cabac初始化时表格的选择取值范围为 [0,2]。 slice_qp_delta 指出用于当前片的所有宏块的量化参数的初始值 QPY SliceQPY26pic_init_qp_minus26slice_qp_delta QPY的取值范围是[0,51]。 我们前文已经提到H.264中量化参数是分图像参数集、片头、宏 块头三层给出的前两层各自给出一个偏移值这个句法元素就是片层的偏移。 sp_for_switch_flag 指出SP帧中的p宏块的解码方式是否是 switching模式。 slice_qs_delta 与slice_qp_delta的与语义相似用在SI和SP中由下式计算 QSY26pic_init_qs_minus26slice_qs_delta QSY的取值是[0,51]。 disable_deblocking_filter_idc H.264规定一套可以在解码器端独立地计算图像中各边界的滤波强度进行滤波的算法。除了解码器独立计算之外编码器也可以传递句法元素来干涉滤波强度这个句法元素指定了在块的边界是否要用滤波同时指明那个块的边界不用块滤波。 slice_alpha_c0_offset_div2 给出用于增强α和tC0的偏移值
FilterOffsetAslice_alpha_c0_offset_div2 1 slice_alpha_c0_offset_div2值的范围是[-6,6]。 slice_beta_offset_div2 当片组的类型是3、4、5时由句法元 素可获得片组中map_units的数目 MapUnitsInSliceGroup0 Min(slice_group_change_cycle×SliceGroupChangeRate, PicSizeIn MapUnits) slice_group_change_cycle由 Ceil(Log2(PicSizeInMapUnits÷SliceGroupChangeRate1))位比特表示 slice_group_change_cycle值的范围是 [0,Ceil(PicSizeInMapUnits÷SliceGroupChangeRate)]。 其中Ceil(x)函数返回不小于x的最小整数。 5、参考图像序列重排序的语义 每一个使用帧间预测的图像都会引用前面已解码的图像作为参考帧。如前文所述编码器给每个参考帧都会分配一个唯一性的标识 即句法元素frame_num。但是当编码器要指定当前图像的参考图像时并不是直接指定该图像的frame_num值而是使用通过下面步骤最终得出的ref_id号如图6.8所示。 其中从frame_num到变换到变量PicNum主要是考虑到场模式的需要当序列中允许出现场时每个非场的图像帧或帧场自适应 解码后必须分解为一个场对从而需要为它们分解后的两个场各自指 定一个标识 进一步从PicNum到ref_id是为了能节省码流因为 PicNum的值通常都比较大而在帧间预测时需要为每个运动矢量都指明相对应的参考帧的标识如果这个标识选用PicNum开销就会比 较大所以H.264又将PicNum映射为一个更小的变量ref_id。 在编码器和解码器都同步地维护一个参考帧队列每解码一个片就将该队列刷新一次把各图像按照特定的规则进行排序排序后各图像在队列中的序号就是该图像的ref_id值。在下文中我们可以看到在宏块层表示参考图像的标识就是ref_id维护队列时两个重要操作重排序Reordering和 标记Marking所用到的句法元素。 说明句法元素的后缀名带有“10”指的是第一个参数列表句法 元素的后缀名带有“11”指的是第二个参数列表用在B帧预测中。 ref_pic_list_reordering_flag_l0 指明是否进行重排序操作。这个句法元素等于1时表明紧跟着会有一系列句法元素用于参考帧队列的重排序。 ref_pic_list_reordering_flag_l1 与上个句法元素的语义相同只是本句法元素用于参考帧队列L1。 reordering_of_pic_nums_idc 与句法元素 abs_diff_pic_num_minus1或long_term_pic_num配合指明执行哪种重排序操作具体语义见表6.23。 abs_diff_pic_num_minus1 在对短期参考帧重排序时指明重排序图像与当前图像预测值的差值取值范围是[0, MaxPicNum1]见表 6.23。 long_term_pic_num 在对长期参考帧重排序时指明重排序图像。 见表6.23。 从上文可以看到每组reordering_of_pic_nums_idc、 abs_diff_pic_num_minus1或reordering_of_pic_nums_idc、 long_term_pic_num只能对一个图像操作而通常情况下都需要对一组图像重排序所以在码流中一般会有个循环反复出现这些重排序的句法元素如图6.9所示循环直到reordering_of_pic_nums_idc等于3结束我们在表6.22中也可以看到这种情况。 6、加权预测的语义 加权预测句法元素的语义如下 luma_log2_weight_denom 给出参考帧列表中参考图像所有亮度的加权系数取值范围是[0,7]。 chroma_log2_weight_denom 给出参考帧列表中参考图像所有色度的加权系数取值范围是[0,7]。 luma_weight_10_flag 其值等于1时指的是在参考图像列表0中的亮度的加权系数存在等于0时在参考图像列表0中的亮度的加权系数不存在。 luma_weight_10[i] 用参考序列0预测亮度值时所用的加权系数取值范围是[-128,127]。如果luma_weight_10_flag is0对于 RefPicList0[i]的luma_weight_10[i] luma_offset_10[i] 用参考序列0预测亮度值时所用的加权系数的额外偏移。luma_offset_10[i] 的取值范围是[–128,127]。如果 luma_weight_10_flag0对于RefPicList0[i]的luma_offset_10[i]0。 chroma_weight_10_flag、chroma_weight_10[i][j]、 chroma_offset_10[i][j]与上述三个类似不同的是这三个句法元素是用在色度预测。 luma_weight_11_flag、luma_weight_11、luma_offset_11、 chroma_weight_11_flag、chroma_weight_11、chroma_offset_11等句法元素与luma_weight_10_flag、luma_weight_10、luma_offset_10、 chroma_weight_10_flag、chroma_weight_10、chroma_offset_10的语义相 似不同点是这几个句法元素是用在参考图像列表1中。 7、参考图像序列标记Marking操作的语义 前文介绍的重排序Reordering操作是对参考帧队列重新排序 而标记Marking操作负责将参考图像移入或移出参考帧队列。 no_output_of_prior_pics_flag 仅在当前图像是IDR图像时出现这个句法元素指明是否要将前面已解码的图像全部输出。 long_term_reference_flag 与上个图像一样仅在当前图像是IDR 图像时出现这一句法元素。这个句法元素指明是否使用长期参考这个机制。如果取值为1表明使用长期参考并且每个IDR图像被解码后自动成为长期参考帧否则取值为0IDR图像被解码后自动成为短期参考帧。 adaptive_ref_pic_marking_mode_flag 指明标记Marking操作的模式具体语义见表6.24。 memory_management_control_operation 在自适应标记 Marking模式中指明本次操作的具体内容具体语义见表6.25。 表6.25 标记Marking操作 difference_of_pic_nums_minus1 当 memory_management_control_operation等于3或1时由这个句法元素可以计算得到需要操作的图像在短期参考队列中的序号。参考帧队列中必须存在这个图像。 long_term_pic_num 当memory_management_control_operation值等于2时从此句法元素中得到所要操作的长期参考图像的序号。 long_term_frame_idx 当memory_management_control_operation等 于3或6分配一个长期参考帧的序号给一个图像。 max_long_term_frame_idx_plus1 此句法元素的值减1指明长期参考队列的最大数目。max_long_term_frame_idx_plus1的取值范围是 [0,num_ref_frames]。 8、片数据的语义 片数据句法的语义如下 cabac_alignment_one_bit 当熵编码模式是CABAC时此时要求 数据字节对齐即数据从下一个字节的第一个比特开始如果还没有字节对齐将出现若干个cabac_alignment_one_bit作为填充。 mb_skip_run 当图像采用帧间预测编码时H.264允许在图像平坦的区域使用“跳跃”块“跳跃”块本身不携带任何数据解码器通过周围已重建的宏块的数据来恢复“跳跃”块。当熵编码为CAVLC或CABAC 时“跳跃”块的表示方法不同。当entropy_coding_mode_flag为1即熵编码为CABAC时是每个“跳跃”块都会有句法元素mb_skip_flag指明 当entropy_coding_mode_flag等于0即熵编码为CAVLC时用一种行程的方法给出紧连着的“跳跃”块的数目即句法元素mb_skip_run。 mb_skip_run的取值范围[0,PicSizeInMbs - CurrMbAddr]。 mb_skip_flag 指明当前宏块是否是跳跃编码模式的宏块。 mb_field_decoding_flag 在帧场自适应图像中指明当前宏块所 属的宏块对是帧模式还是场模式。mb_field_decoding_flag等于0则当前宏块对是帧宏块对等于1该宏块对是一个场宏块对。如果一个宏块对的两个宏块句法结构中都没有出现这个句法元素即它们都是“跳 跃”块时本句法元素由以下决定
如果这个宏块对与相邻的、左边的宏块对属于同一个片时 这个宏块对的mb_field_decoding_flag的值等于左边的宏块对的 mb_field_decoding_flag的值否则这个宏块对的mb_field_decoding_flag的值等于上边同属于一个片的宏块对的mb_field_decoding_flag的值如果当前宏块对的左边和上方都没有邻居宏块对这个宏块对的mb_field_decoding_flag的值等于0即帧模式。 9、宏块层的语义 mb_type 指明当前宏块的类型。H.264规定不同的片中允许出现的宏块类型也不同。在各种片类型中允许出现的宏块种类见表6.26。 可以看到I片中只允许出现I宏块而P片中既可以出现P宏块也可以出现I宏块。也就是说在帧间预测的图像中也可以包括帧内预测的图像。其他片也有类似情况。 更进一步来看H.264也定义了比以往视频编码标准更多的宏块类型。具体来说在帧间预测模式下宏块可以有7种运动矢量的划分方法。在帧内预测模式下如果是帧内16×16预测宏块有4种预测方法即4种类型如果是帧内4×4预测这时每个4×4块可以有9种预测方法整个宏块共有144种类型。 mb_type并不能描述以上所有有关宏块类型的信息。mb_type作为出现在宏块层的第一个句法元素它描述跟整个宏块有关的基本的类型信息。在不同的片中mb_type的定义是不同的下面我们分别讨论 I、P、B片中这个句法元素的意义。 1I片中的mb_type具体语义见表6.27。 表中Intra_4×4表示使用帧内4×4预测Intra_16×16表示使用帧内 16×16预测。当使用帧内16×16时类型名称由了如下的结构组成
I_16×16_x_y_z 其中x对应于表中“帧内16×16的预测模式”字段的值y对应于表中“色度CBP”字段的值z对应于表中“亮度CBP”的值。
①帧内16×16的预测模式当使用帧内16×16预测时指定使用何种预测方式帧内16×16共有4种预测模式。②CodedBlockPatternLuma指定当前宏块色度分量的CBP CBPCodedBlockPattern是指子宏块残差的编码方案。该变量详细语 义见coded_block_pattern条目。③亮度CBP指定当前宏块亮度分量的CBP详细语义见coded_block_pattern条目。 我们看到帧内16×16宏块类型的mb_type语义原比其他宏块类型的复杂这是因为当使用帧内16×16时整个宏块是一个统一的整体 宏块中各子宏块、4×4小块的预测模式信息都是相同的所以可以把这些信息放入mb_type以减少码流。其他宏块类型的这些信息必须在各子块中另外用句法元素指明。 2P片中的mb_type具体语义见表6.28。 在表6.28中Pred_L0表示用参考列表L0即前向预测。P片中的宏块类型可参见表6.28和表6.27。当mb_type值为04时mb_type的含义见表6.28当mb_type值为530时mb_type的含义见表6.27用 mb_type值减5所得值来进行查找。预测模式mb_typen预测模式是mb_type的函数n是宏块的第n个分区。 3B片中的mb_type其具体语义见表6.29。 B片中的宏块类型可参见表6.29和表6.27。如果当前宏块是属于B片且mb_type值为022mb_type的含义见表6.29当mb_type值为2348 时mb_type的含义见表6.27用mb_type值减23所得值来进行查找。 表中Pred_L0表示使用L0即前向预测Pred_L1表示使用L1 即后向预测Bipred表示双向预测Direct表示直接预测模式。预测模式mb_type,n是mb_type的函数n是宏块的第n个分区。 pcm_alignment_zero_bit 其值等于0。 pcm_byte[i] 像素值。前256个pcm_byte[i]的值代表亮度像素的值下一个(256×(ChromaFormatFactor1))/2个pcm_byte[i]的值代表Cb分量的值。最后一个(256×(Chroma FormatFactor1))/2个pcm_byte[i]的 值代表Cr分量的值。 coded_block_pattern 即CBP指亮度和色度分量的各小块的残差的编码方案所谓编码方案有以下几种 1所有残差包括DC、AC都编码 2只对DC系数编码 3所有残差包括DC、AC都不编码。 这个句法元素同时隐含了一个宏块中亮度、色度分量的CBP所以第一步必须先分别解出各分量各自CBP的值。其中两个色度分量的CBP是相同的。变量CodedBlockPatternLuma是亮度分量的CBP变量CodedBlocPatternChroma是色度分量的CBP。 对于非Intra_16×16的宏块类型
CodedBlockPatternLumacoded_block_pattern 16
CodedBlockPatternChromacoded_block_pattern / 16 对于Intra_16×16宏块类型CodedBlockPatternLuma 和 CodedBlockPatternChroma的值不是由本句法元素给出而是通过mb_type得到。
①CodedBlockPatternLuma是一个16位的变量其中只有最低四位有定义。由于非Intra_16×16的宏块不单独编码DC系数所以这个变量只指明两种编码方案残差全部编码或全部不编码。变量的最低位比特从最低位开始每一位对应一个子宏块该位等于1时表明对应子宏块残差系数被传送该位等于0时表明对应子宏块残差全部不被传送解码器把这些残差系数赋为0。②CodedBlockPatternChroma当值为0、1、2时有定义见表 6.30。
表6.30 CodedBlockPatternChroma的定义 mb_qp_delta 在宏块层中的量化参数的偏移值。mb_qp_delta值的范围是[-26,25]。量化参数是在图像参数集、片头、宏块分三层给出的最终用于解码的量化参数由以下公式得到 其中是当前宏块按照解码顺序排列的前一个宏块的量化参数我们可以看到mb_qp_delta所指示的偏移是前后两个宏块之间的偏移。片中第一个宏块的是由下式给出 10、宏块预测的语义 prev_intra4×4_pred_mode_flag[luma4×4BlkIdx] rem_intra4×4_pred_mode[luma4×4BlkIdx] 帧内预测模式也是可以预测的句法元素 prev_intra4×4_pred_mode_flag用来指明帧内预测中亮度分量的预测模式的预测值是否是实际预测模式。如果是就不需另外再传预测模式 如果不是就由rem_intra4×4_pred_mode指定实际预测模式。 intra_chroma_pred_mode 在帧内预测时指定色度的预测模式具体语义见表6.31。 ref_idx_10[mbPartIdx] 用参考帧列表L0进行预测时即前向预测时参考图像在参考帧列表中的序号。其中mbPartIdx是宏块分区的序号。 如果当前宏块是非场宏块则ref_idx_10[mbPartIdx]值的范围是[0, num_ref_idx_10_active_minus1]。 如果当前宏块是场宏块宏块所在图像是场当图像是帧场自适应时当前宏块处于场编码的宏块对ref_idx_10[mbPartIdx]值的范围 是0到2×num_ref_idx_10_active_minus11如前所述此时参考帧队列的帧都将拆成场故参考队列长度加倍。 ref_idx_11[mbPartIdx] 语义同上只是这个句法元素用于参考帧 列表L1即后向预测。 mvd_10[mbPartIdx][0][compIdx] 运动矢量的预测值和实际值之 间的差。mbPartIdx是宏块分区的序号。compIdx0时为水平运动矢量compIdx1时为垂直运动矢量。 mvd_11[mbPartIdx][0][compIdx] 语义同上只是这个句法元素 用于L1即后向预测。 11、子宏块预测的语义 子宏块预测的句法元素的语法如下 sub_mb_type[mbPartIdx] 指明子宏块的预测类型在不同的宏块类型中这个句法元素的语义不一样。 1P宏块 P宏块中的子宏块类型见表6.32。 2B宏块 B宏块中的子宏块类型见表6.33。 表6.33 B宏块中的子宏块类型 ref_idx_10[mbPartIdx] 与10节中定义的ref_idx_10相同。 ref_idx_11[mbPartIdx] 与10节中定义的ref_idx_11相同。 mvd_10[mbPartIdx][subMbPartIdx][compIdx] 与10节中定义 mvd_10相同除了subMbPartIdx子宏块中的索引值。 mvd_11[mbPartIdx][subMbPartIdx][compIdx] 同上用于L1。 12、用CAVLC方式编码的残差数据的语义 coeff_token 指明了非零系数的个数拖尾系数的个数。 trailing_ones_sign_flag 拖尾系数的正负符号。 如果trailing_ones_sign_flag0相应的拖尾系数是1 如果trailing_ones_sign_flag1相应的拖尾系数是-1。 level_prefix和level_suffix 分别表示非零系数值的前缀和后缀。 total_zeros 系数中0的总个数。 run_before 在非零系数之前连续零的个数。 13、用CABAC方式编码的残差数据的语义 coded_block_flag 指出当前块是否包含非零系数。 如果coded_block_flag0这个块不包含非零系数 如果coded_block_flag1这个块包含非零系数。 significant_coeff_flag[i] 指出在位置为i处的变换系数是否为零。 如果significant_coeff_flag[i]0在位置为i处的变换系数为零 如果significant_coeff_flag[i]1在位置为i处的变换系数不为零。 last_significant_coeff_flag[i] 表示当前位置i处的变换系数是否为块中最后一个非零系数。 如果last_significant_coeff_flag[i]1这个块中随后的系数都为零 否则这个块中随后的系数中还有其他的非零系数 coeff_abs_level_minus1[i] 系数的绝对值减1。 coeff_sign_flag[i] 系数的符号位。 coeff_sign_flag0正数 coeff_sign_flag1负数。 14、总结 H.264 的句法是经过精心设计的构成句法的各句法元素既相互依赖又相互独立。依赖是为了减少冗余信息提高编码效率而独立是为了使通信更加可靠在错误发生时限制错误的扩散。但是依赖和独立又是矛盾的往往要在它们之间作取舍。在实现H.264的编解码器时同样有速度和健壮性这样一组矛盾的关系既要在现有硬件平台上提高编解码的速度又要作充分的安全检查建立异常处理机制。 如果能详细考察句法元素间的依赖与独立关系充分利用H.264各句法 元素间本身的内在联系与安全机制便能够设计出既快速又安全、 健壮的编解码算法。 四、H.264语句语义代码实现解析 以下是一个demo例子实现读取out.h264的H264流的语句语义。 解析代码全过程在 github地址为https://github.com/wangyongyao1989/WyFFmpeg/tree/main/h264 代码截图 Java层代码 向JNI层传入out.h264文件的路径
package com.example.myyffmpeg.fragment;import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.FrameLayout;import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProviders;import com.example.myyffmpeg.FFViewModel;
import com.example.myyffmpeg.databinding.FragmentH264LayoutBinding;
import com.wangyongyao.common.utils.FileUtils;
import com.wangyongyao.h264.H264CallJni;
import com.wangyongyao.h264.view.X264CameraPreView;/*** author : wangyongyao https://github.com/wangyongyao1989* Create Time : 2025/3/20 16:04* Descibe : MyyFFmpeg com.example.myyffmpeg.fragment*/
public class H264Fragment extends BaseFragment {private static final String TAG H264Fragment.class.getSimpleName();private Button mH264Back;private FFViewModel mFfViewModel;private H264CallJni mH264CallJni;private Button mBtn1;private FragmentH264LayoutBinding mBinding;private Button mBtn2;private FrameLayout mX264Show;private X264CameraPreView mX264CameraPreView;Overridepublic View getLayoutDataBing(NonNull LayoutInflater inflater, Nullable ViewGroup container, Nullable Bundle savedInstanceState) {mBinding FragmentH264LayoutBinding.inflate(inflater);return mBinding.getRoot();}Overridepublic void initView() {mH264Back mBinding.btnHevcH264Back;mBtn1 mBinding.btnH2641;mBtn2 mBinding.btnH2642;mX264Show mBinding.x264Show;}Overridepublic void initData() {mH264CallJni new H264CallJni();}Overridepublic void initObserver() {mFfViewModel ViewModelProviders.of(requireActivity()).get(FFViewModel.class);}Overridepublic void initListener() {mH264Back.setOnClickListener(view - {mFfViewModel.getSwitchFragment().postValue(FFViewModel.FRAGMENT_STATUS.MAIN);});mBtn1.setOnClickListener(view - {String h264FilePath FileUtils.getModelFilePath(getActivity(), out.h264);//向JNI层传入out.h264文件的路径mH264CallJni.testH264(h264FilePath);});mBtn2.setOnClickListener(view - {if (mX264CameraPreView null) {mX264CameraPreView new X264CameraPreView(getActivity());}mX264Show.addView(mX264CameraPreView);});}} JNI层代码
#include jni.h
#include string
#include android/log.h#include h264include/H264LogUtils.h
#include h264include/H264NaluParse.h// Author : wangyongyao https://github.com/wangyongyao1989
// Created by MMM on 2025/3/20.
//using namespace std;
//包名类名字符串定义
const char *java_call_jni_class com/wangyongyao/h264/H264CallJni;H264NaluParse *h264NaluParse;extern C
JNIEXPORT void JNICALL
cpp_test_h264(JNIEnv *env, jobject thiz, jstring dataPath) {const char *cDataPath env-GetStringUTFChars(dataPath, nullptr);LOGE(cpp_test_h264%s, cDataPath);if (h264NaluParse nullptr) {h264NaluParse new H264NaluParse();}h264NaluParse-setHevcNalDataPath(cDataPath);env-ReleaseStringUTFChars(dataPath, cDataPath);}static const JNINativeMethod methods[] {{n_test_h264, (Ljava/lang/String;)V, (void *) cpp_test_h264},
};// 定义注册方法
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {LOGD(动态注册);JNIEnv *env;if ((vm)-GetEnv((void **) env, JNI_VERSION_1_6) ! JNI_OK) {LOGD(动态注册GetEnv fail);return JNI_ERR;}// 获取类引用jclass clazz env-FindClass(java_call_jni_class);// 注册native方法jint regist_result env-RegisterNatives(clazz, methods,sizeof(methods) / sizeof(methods[0]));if (regist_result) { // 非零true 进ifLOGE(动态注册 fail regist_result %d, regist_result);} else {LOGI(动态注册 success result %d, regist_result);}return JNI_VERSION_1_6;
}extern C void JNI_OnUnload(JavaVM *jvm, void *p) {JNIEnv *env NULL;if (jvm-GetEnv((void **) (env), JNI_VERSION_1_6) ! JNI_OK) {return;}jclass clazz env-FindClass(java_call_jni_class);if (env ! NULL) {env-UnregisterNatives(clazz);}} C/C层代码 H264NaluParse.cpp H264NaluParse.cpp中获取从Java层传入的文件路径之后打开文件并读取out.h264数据到std::vectoruint8_t buffer集合中
// Author : wangyongyao https://github.com/wangyongyao1989
// Created by MMM on 2025/3/20.
//#include h264include/H264NaluParse.h
#include h264include/h264nal/h264_bitstream_parser.hH264NaluParse::H264NaluParse() {bitstream_parser_state new h264nal::H264BitstreamParserState;
}H264NaluParse::~H264NaluParse() {release();
}int H264NaluParse::setHevcNalDataPath(const char *dataPath) {// read infile// TODO(chemag): read the infile incrementallyFILE *infp fopen(dataPath, rb);if (infp nullptr) {// did not workfprintf(stderr, Could not open input file: \%s\\n, dataPath);return -1;}fseek(infp, 0, SEEK_END);int64_t size ftell(infp);fseek(infp, 0, SEEK_SET);// read file into bufferstd::vectoruint8_t buffer(size);fread(reinterpret_castchar *(buffer.data()), 1, size, infp);fclose(infp);LOGE(buffer.data() :%d, buffer.size());// parse bitstreamstd::unique_ptrh264nal::H264BitstreamParser::BitstreamState bitstream h264nal::H264BitstreamParser::ParseBitstream(buffer.data(), buffer.size(), bitstream_parser_state, false);uint32_t pic_height_in_map_units_minus1 bitstream_parser_state-GetSps(0)-sps_data-pic_height_in_map_units_minus1;uint32_t pic_width_in_mbs_minus1 bitstream_parser_state-GetSps(0)-sps_data-pic_width_in_mbs_minus1;uint32_t frame_mbs_only_flag bitstream_parser_state-GetSps(0)-sps_data-frame_mbs_only_flag;// 计算宽高int width (pic_width_in_mbs_minus1 1) * 16;int height (pic_height_in_map_units_minus1 1) * 16 * (2 - frame_mbs_only_flag);//宽高LOGE(SPS:height %d, height);LOGE(SPS:width %d, width);//色度格式LOGE(SPS:chroma_format_idc %d,bitstream_parser_state-GetSps(0)-sps_data-chroma_format_idc);uint32_t frameRate;uint32_t vuiParametersPresentFlag bitstream_parser_state-GetSps(0)-sps_data-vui_parameters-time_scale;if (vuiParametersPresentFlag) {uint32_t numUnitsInTick bitstream_parser_state-GetSps(0)-sps_data-vui_parameters-num_units_in_tick;uint32_t timeScale bitstream_parser_state-GetSps(0)-sps_data-vui_parameters-time_scale;if (numUnitsInTick 0) {frameRate timeScale / (2.0 * numUnitsInTick); // 帧率公式LOGE(SPS: frameRate%d, frameRate);} else {frameRate 30;}} else {frameRate 30;}//帧率LOGE(SPS:KEY_FRAME_RATE %d, frameRate);return 0;
}void H264NaluParse::release() {if (bitstream_parser_state) {delete bitstream_parser_state;bitstream_parser_state nullptr;}
}h264_bitstream_parser.h: 该类解析出H.264流中的Nalu存放在std::vectorNaluIndex 的集合中。 BitstreamState结构体
struct BitstreamState {BitstreamState() default;~BitstreamState() default;// disable copy ctor, move ctor, and copymove assignmentsBitstreamState(const BitstreamState) delete;BitstreamState(BitstreamState) delete;BitstreamState operator(const BitstreamState) delete;BitstreamState operator(BitstreamState) delete;#ifdef FDUMP_DEFINEvoid fdump(FILE* outfp, int indent_level) const;
#endif // FDUMP_DEFINEbool add_offset;bool add_length;bool add_parsed_length;bool add_checksum;// NAL unitsstd::vectorstd::unique_ptrstruct H264NalUnitParser::NalUnitStatenal_units;}; NaluIndex结构体
struct NaluIndex {// Start index of NALU, including start sequence.size_t start_offset;// Start index of NALU payload, typically type header.size_t payload_start_offset;// Length of NALU payload, in bytes, counting from payload_start_offset.size_t payload_size;}; h264_bitstream_parser.cc
/** Copyright (c) Facebook, Inc. and its affiliates.*/#include ../h264include/h264nal/h264_bitstream_parser.h#include stdio.h#include cstdint
#include memory
#include vector#include ../h264include/h264nal/h264_bitstream_parser_state.h
#include ../h264include/h264nal/h264_common.h
#include ../h264include/h264nal/h264_nal_unit_parser.hnamespace {
// The size of a full NALU start sequence {0 0 0 1}, used for the first NALU
// of an access unit, and for SPS, PPS, and SubsetSPS blocks.
// const size_t kNaluLongStartSequenceSize 4;
// The size of a shortened NALU start sequence {0 0 1}, that may be used if
// not the first NALU of an access unit or an SPS, PPS, or SubsetSPS block.
const size_t kNaluShortStartSequenceSize 3;
} // namespacenamespace h264nal {// General note: this is based off the 2012 version of the H.264 standard.
// You can find it on this page:
// http://www.itu.int/rec/T-REC-H.264std::vectorH264BitstreamParser::NaluIndex
H264BitstreamParser::FindNaluIndices(const uint8_t* data,size_t length) noexcept {// This is sorta like Boyer-Moore, but with only the first optimization step:// given a 3-byte sequence were looking at, if the 3rd byte isnt 1 or 0,// skip ahead to the next 3-byte sequence. 0s and 1s are relatively rare, so// this will skip the majority of reads/checks.std::vectorNaluIndex sequences;if (length kNaluShortStartSequenceSize) {return sequences;}const size_t end length - kNaluShortStartSequenceSize;for (size_t i 0; i end;) {if (data[i 2] 1) {i 3;} else if (data[i 2] 0x01 data[i 1] 0x00 data[i] 0x00) {// We found a start sequence, now check if it was a 3 of 4 byte one.NaluIndex index {i, i 3, 0};if (index.start_offset 0 data[index.start_offset - 1] 0)--index.start_offset;// Update length of previous entry.auto it sequences.rbegin();if (it ! sequences.rend())it-payload_size index.start_offset - it-payload_start_offset;sequences.push_back(index);i 3;} else {i;}}// Update length of last entry, if any.auto it sequences.rbegin();if (it ! sequences.rend())it-payload_size length - it-payload_start_offset;return sequences;
}// Parse a raw (RBSP) buffer with explicit NAL unit separator (3- or 4-byte
// sequence start code prefix). Function splits the stream in NAL units,
// and then parses each NAL unit. For that, it unpacks the RBSP inside
// each NAL unit buffer, and adds the corresponding parsed struct into
// the bitstream list (a BitstreamState object).
// Function returns the said bitstream list.
std::unique_ptrH264BitstreamParser::BitstreamState
H264BitstreamParser::ParseBitstream(const uint8_t* data, size_t length,H264BitstreamParserState* bitstream_parser_state,bool add_checksum) noexcept {auto bitstream std::make_uniqueBitstreamState();// (1) split the input string into a vector of NAL unitsstd::vectorNaluIndex nalu_indices FindNaluIndices(data, length);// process each of the NAL unitsfor (const NaluIndex nalu_index : nalu_indices) {// (2) parse the NAL units, and add them to the vectorauto nal_unit H264NalUnitParser::ParseNalUnit(data[nalu_index.payload_start_offset], nalu_index.payload_size,bitstream_parser_state, add_checksum);if (nal_unit nullptr) {// cannot parse the NalUnit
#ifdef FPRINT_ERRORSfprintf(stderr, error: cannot parse buffer into NalUnit\n);
#endif // FPRINT_ERRORScontinue;}// store the offsetnal_unit-offset nalu_index.payload_start_offset;nal_unit-length nalu_index.payload_size;bitstream-nal_units.push_back(std::move(nal_unit));}return bitstream;
}std::unique_ptrH264BitstreamParser::BitstreamState
H264BitstreamParser::ParseBitstream(const uint8_t* data, size_t length,bool add_offset, bool add_length,bool add_parsed_length,bool add_checksum) noexcept {// keep a bitstream parser state (to keep the SPS/PPS/SubsetSPS NALUs)H264BitstreamParserState bitstream_parser_state;// create bitstream parser stateauto bitstream std::make_uniqueBitstreamState();// parse the filebitstream ParseBitstream(data, length, bitstream_parser_state, add_checksum);if (bitstream nullptr) {// did not work
#ifdef FPRINT_ERRORSfprintf(stderr, Could not init h264 bitstream parser\n);
#endif // FPRINT_ERRORSreturn nullptr;}bitstream-add_offset add_offset;bitstream-add_length add_length;bitstream-add_parsed_length add_parsed_length;bitstream-add_checksum add_checksum;return bitstream;
}#ifdef FDUMP_DEFINE
void H264BitstreamParser::BitstreamState::fdump(FILE* outfp,int indent_level) const {for (auto nal_unit : nal_units) {nal_unit-fdump(outfp, indent_level, add_offset, add_length,add_parsed_length, add_checksum);fprintf(outfp, \n);}
}
#endif // FDUMP_DEFINE} // namespace h264nalParseBitstream()函数 从H.264流中解压缩RBSP并从提供的缓冲区解析比特流状态。 FindNaluIndices()函数 通过0x000001或0x00000001起始码为标志切割出Nalu h264_nal_unit_header_parser.cc 对应的语句语义为第三节中的NAL层语义。代码如下
/** Copyright (c) Facebook, Inc. and its affiliates.*/#include ../h264include/h264nal/h264_nal_unit_header_parser.h#include stdio.h#include cstdint
#include memory
#include vector#include ../h264include/h264nal/h264_bitstream_parser_state.h
#include ../h264include/h264nal/h264_common.hnamespace h264nal {// General note: this is based off the 2012 version of the H.264 standard.
// You can find it on this page:
// http://www.itu.int/rec/T-REC-H.264// Unpack RBSP and parse NAL Unit header state from the supplied buffer.std::unique_ptrH264NalUnitHeaderParser::NalUnitHeaderStateH264NalUnitHeaderParser::ParseNalUnitHeader(const uint8_t *data,size_t length) noexcept {std::vectoruint8_t unpacked_buffer UnescapeRbsp(data, length);rtc264::BitBuffer bit_buffer(unpacked_buffer.data(), unpacked_buffer.size());return ParseNalUnitHeader(bit_buffer);}bool H264NalUnitHeaderParser::GetNalUnitType(const uint8_t *data,const size_t length,NalUnitType naluType) noexcept {rtc264::BitBuffer bitBuffer(data, length);auto naluHeader ParseNalUnitHeader(bitBuffer);if (!naluHeader) {return false;}naluType static_castNalUnitType(naluHeader-nal_unit_type);return true;}std::unique_ptrH264NalUnitHeaderParser::NalUnitHeaderStateH264NalUnitHeaderParser::ParseNalUnitHeader(rtc264::BitBuffer *bit_buffer) noexcept {// H264 NAL Unit Header (nal_unit_header()) parser.// Section 7.3.1.2 (NAL unit header syntax) of the H.264// standard for a complete description.auto nal_unit_header std::make_uniqueNalUnitHeaderState();// forbidden_zero_bit f(1)if (!bit_buffer-ReadBits(nal_unit_header-forbidden_zero_bit, 1)) {return nullptr;}// nal_ref_idc u(2)if (!bit_buffer-ReadBits(nal_unit_header-nal_ref_idc, 2)) {return nullptr;}// nal_unit_type u(5)if (!bit_buffer-ReadBits(nal_unit_header-nal_unit_type, 5)) {return nullptr;}if (nal_unit_header-nal_unit_type 14 ||nal_unit_header-nal_unit_type 20 ||nal_unit_header-nal_unit_type 21) {if (nal_unit_header-nal_unit_type ! 21) {// svc_extension_flag u(1)if (!bit_buffer-ReadBits(nal_unit_header-svc_extension_flag, 1)) {return nullptr;}if (nal_unit_header-svc_extension_flag 1) {// nal_unit_header_svc_extension()nal_unit_header-nal_unit_header_svc_extension H264NalUnitHeaderSvcExtensionParser::ParseNalUnitHeaderSvcExtension(bit_buffer);if (nal_unit_header-nal_unit_header_svc_extension nullptr) {return nullptr;}}} else {// avc_3d_extension_flag u(1)if (!bit_buffer-ReadBits(nal_unit_header-avc_3d_extension_flag, 1)) {return nullptr;}}}return nal_unit_header;}#ifdef FDUMP_DEFINEvoid H264NalUnitHeaderParser::NalUnitHeaderState::fdump(FILE* outfp, int indent_level) const {fprintf(outfp, nal_unit_header {);indent_level indent_level_incr(indent_level);fdump_indent_level(outfp, indent_level);fprintf(outfp, forbidden_zero_bit: %i, forbidden_zero_bit);fdump_indent_level(outfp, indent_level);fprintf(outfp, nal_ref_idc: %i, nal_ref_idc);fdump_indent_level(outfp, indent_level);fprintf(outfp, nal_unit_type: %i, nal_unit_type);if (nal_unit_type 14 || nal_unit_type 20 || nal_unit_type 21) {if (nal_unit_type ! 21) {fdump_indent_level(outfp, indent_level);fprintf(outfp, svc_extension_flag: %i, svc_extension_flag);if (svc_extension_flag nal_unit_header_svc_extension ! nullptr) {fdump_indent_level(outfp, indent_level);nal_unit_header_svc_extension-fdump(outfp, indent_level);}} else {fdump_indent_level(outfp, indent_level);fprintf(outfp, avc_3d_extension_flag: %i, avc_3d_extension_flag);}}indent_level indent_level_decr(indent_level);fdump_indent_level(outfp, indent_level);fprintf(outfp, });}
#endif // FDUMP_DEFINE} // namespace h264nalh264_sps_parser.cc 对应的语句语义为第三节中的序列参数集语义代码如下
/** Copyright (c) Facebook, Inc. and its affiliates.*/#include ../h264include/h264nal/h264_sps_parser.h#define __STDC_FORMAT_MACROS#include inttypes.h
#include stdio.h#include cmath
#include cstdint
#include memory
#include vector#include ../h264include/h264nal/h264_common.h
#include ../h264include/h264nal/h264_vui_parameters_parser.hnamespace h264nal {// General note: this is based off the 2012 version of the H.264 standard.
// You can find it on this page:
// http://www.itu.int/rec/T-REC-H.264// Unpack RBSP and parse SPS data state from the supplied buffer.std::unique_ptrH264SpsDataParser::SpsDataStateH264SpsDataParser::ParseSpsData(const uint8_t *data, size_t length) noexcept {std::vectoruint8_t unpacked_buffer UnescapeRbsp(data, length);rtc264::BitBuffer bit_buffer(unpacked_buffer.data(), unpacked_buffer.size());return ParseSpsData(bit_buffer);}std::unique_ptrH264SpsDataParser::SpsDataStateH264SpsDataParser::ParseSpsData(rtc264::BitBuffer *bit_buffer) noexcept {uint32_t bits_tmp;int32_t golomb_tmp;// H264 SPS Nal Unit (seq_parameter_set_data(()) parser.// Section 7.3.2.1.1 (Sequence parameter set data syntax) of the H.264// standard for a complete description.auto sps_data std::make_uniqueSpsDataState();// profile_idc u(8)if (!bit_buffer-ReadBits(sps_data-profile_idc, 8)) {return nullptr;}// constraint_set0_flag u(1)if (!bit_buffer-ReadBits(sps_data-constraint_set0_flag, 1)) {return nullptr;}// constraint_set1_flag u(1)if (!bit_buffer-ReadBits(sps_data-constraint_set1_flag, 1)) {return nullptr;}// constraint_set2_flag u(1)if (!bit_buffer-ReadBits(sps_data-constraint_set2_flag, 1)) {return nullptr;}// constraint_set3_flag u(1)if (!bit_buffer-ReadBits(sps_data-constraint_set3_flag, 1)) {return nullptr;}// constraint_set4_flag u(1)if (!bit_buffer-ReadBits(sps_data-constraint_set4_flag, 1)) {return nullptr;}// constraint_set5_flag u(1)if (!bit_buffer-ReadBits(sps_data-constraint_set5_flag, 1)) {return nullptr;}// reserved_zero_2bits u(2)if (!bit_buffer-ReadBits(sps_data-reserved_zero_2bits, 2)) {return nullptr;}// level_idc u(8)if (!bit_buffer-ReadBits(sps_data-level_idc, 8)) {return nullptr;}// seq_parameter_set_id ue(v)if (!bit_buffer-ReadExponentialGolomb((sps_data-seq_parameter_set_id))) {return nullptr;}if (sps_data-profile_idc 100 || sps_data-profile_idc 110 ||sps_data-profile_idc 122 || sps_data-profile_idc 244 ||sps_data-profile_idc 44 || sps_data-profile_idc 83 ||sps_data-profile_idc 86 || sps_data-profile_idc 118 ||sps_data-profile_idc 128 || sps_data-profile_idc 138 ||sps_data-profile_idc 139 || sps_data-profile_idc 134 ||sps_data-profile_idc 135) {// chroma_format_idc ue(v)if (!bit_buffer-ReadExponentialGolomb((sps_data-chroma_format_idc))) {return nullptr;}if (sps_data-chroma_format_idc 3) {// separate_colour_plane_flag u(1)if (!bit_buffer-ReadBits(sps_data-separate_colour_plane_flag, 1)) {return nullptr;}}// bit_depth_luma_minus8 ue(v)if (!bit_buffer-ReadExponentialGolomb((sps_data-bit_depth_luma_minus8))) {return nullptr;}// bit_depth_chroma_minus8 ue(v)if (!bit_buffer-ReadExponentialGolomb((sps_data-bit_depth_chroma_minus8))) {return nullptr;}// qpprime_y_zero_transform_bypass_flag u(1)if (!bit_buffer-ReadBits(sps_data-qpprime_y_zero_transform_bypass_flag,1)) {return nullptr;}// seq_scaling_matrix_present_flag u(1)if (!bit_buffer-ReadBits(sps_data-seq_scaling_matrix_present_flag, 1)) {return nullptr;}if (sps_data-seq_scaling_matrix_present_flag) {for (uint32_t i 0; i ((sps_data-chroma_format_idc ! 3) ? 8 : 12);i) {// seq_scaling_list_present_flag[i] u(1)if (!bit_buffer-ReadBits(bits_tmp, 1)) {return nullptr;}sps_data-seq_scaling_list_present_flag.push_back(bits_tmp);if (sps_data-seq_scaling_list_present_flag[i]) {// scaling_list()if (i 6) {(void) sps_data-scaling_list(bit_buffer, i, sps_data-ScalingList4x4, 16,sps_data-UseDefaultScalingMatrix4x4Flag);} else {(void) sps_data-scaling_list(bit_buffer, i - 6, sps_data-ScalingList8x8, 64,sps_data-UseDefaultScalingMatrix4x4Flag);}}}}}// log2_max_frame_num_minus4 ue(v)if (!bit_buffer-ReadExponentialGolomb((sps_data-log2_max_frame_num_minus4))) {return nullptr;}// pic_order_cnt_type ue(v)if (!bit_buffer-ReadExponentialGolomb((sps_data-pic_order_cnt_type))) {return nullptr;}if (sps_data-pic_order_cnt_type 0) {// log2_max_pic_order_cnt_lsb_minus4 ue(v)if (!bit_buffer-ReadExponentialGolomb((sps_data-log2_max_pic_order_cnt_lsb_minus4))) {return nullptr;}} else if (sps_data-pic_order_cnt_type 1) {// delta_pic_order_always_zero_flag u(1)if (!bit_buffer-ReadBits(sps_data-delta_pic_order_always_zero_flag, 1)) {return nullptr;}// offset_for_non_ref_pic se(v)if (!bit_buffer-ReadSignedExponentialGolomb((sps_data-offset_for_non_ref_pic))) {return nullptr;}// offset_for_top_to_bottom_field se(v)if (!bit_buffer-ReadSignedExponentialGolomb((sps_data-offset_for_top_to_bottom_field))) {return nullptr;}// num_ref_frames_in_pic_order_cnt_cycle ue(v)if (!bit_buffer-ReadExponentialGolomb((sps_data-num_ref_frames_in_pic_order_cnt_cycle))) {return nullptr;}for (uint32_t i 0; i sps_data-num_ref_frames_in_pic_order_cnt_cycle;i) {// offset_for_ref_frame[i] se(v)if (!bit_buffer-ReadSignedExponentialGolomb(golomb_tmp)) {return nullptr;}sps_data-offset_for_ref_frame.push_back(golomb_tmp);}}// max_num_ref_frames ue(v)if (!bit_buffer-ReadExponentialGolomb((sps_data-max_num_ref_frames))) {return nullptr;}// gaps_in_frame_num_value_allowed_flag u(1)if (!bit_buffer-ReadBits(sps_data-gaps_in_frame_num_value_allowed_flag,1)) {return nullptr;}// pic_width_in_mbs_minus1 ue(v)if (!bit_buffer-ReadExponentialGolomb((sps_data-pic_width_in_mbs_minus1))) {return nullptr;}// pic_height_in_map_units_minus1 ue(v)if (!bit_buffer-ReadExponentialGolomb((sps_data-pic_height_in_map_units_minus1))) {return nullptr;}// frame_mbs_only_flag u(1)if (!bit_buffer-ReadBits(sps_data-frame_mbs_only_flag, 1)) {return nullptr;}if (!sps_data-frame_mbs_only_flag) {// mb_adaptive_frame_field_flag u(1)if (!bit_buffer-ReadBits(sps_data-mb_adaptive_frame_field_flag, 1)) {return nullptr;}}// direct_8x8_inference_flag u(1)if (!bit_buffer-ReadBits(sps_data-direct_8x8_inference_flag, 1)) {return nullptr;}// frame_cropping_flag u(1)if (!bit_buffer-ReadBits(sps_data-frame_cropping_flag, 1)) {return nullptr;}if (sps_data-frame_cropping_flag) {// frame_crop_left_offset ue(v)if (!bit_buffer-ReadExponentialGolomb((sps_data-frame_crop_left_offset))) {return nullptr;}// frame_crop_right_offset ue(v)if (!bit_buffer-ReadExponentialGolomb((sps_data-frame_crop_right_offset))) {return nullptr;}// frame_crop_top_offset ue(v)if (!bit_buffer-ReadExponentialGolomb((sps_data-frame_crop_top_offset))) {return nullptr;}// frame_crop_bottom_offset ue(v)if (!bit_buffer-ReadExponentialGolomb((sps_data-frame_crop_bottom_offset))) {return nullptr;}}// vui_parameters_present_flag u(1)if (!bit_buffer-ReadBits((sps_data-vui_parameters_present_flag), 1)) {return nullptr;}if (sps_data-vui_parameters_present_flag) {// vui_parameters()sps_data-vui_parameters H264VuiParametersParser::ParseVuiParameters(bit_buffer);if (sps_data-vui_parameters nullptr) {return nullptr;}}return sps_data;}uint32_t H264SpsDataParser::SpsDataState::getChromaArrayType() noexcept {// Rec. ITU-T H.264 (2012) Page 74, Section 7.4.2.1.1// the value of the variable ChromaArrayType is assigned as follows:// - If separate_colour_plane_flag is equal to 0, ChromaArrayType is set// equal to chroma_format_idc.// - Otherwise (separate_colour_plane_flag is equal to 1), ChromaArrayType// is set equal to 0.uint32_t ChromaArrayType 0;if (separate_colour_plane_flag 0) {ChromaArrayType chroma_format_idc;} else {ChromaArrayType 0;}return ChromaArrayType;}// Section 7.3.2.1.1.1bool H264SpsDataParser::SpsDataState::scaling_list(rtc264::BitBuffer *bit_buffer, uint32_t i, std::vectoruint32_t scalingList,uint32_t sizeOfScalingList,std::vectoruint32_t useDefaultScalingMatrixFlag) noexcept {uint32_t lastScale 8;uint32_t nextScale 8;for (uint32_t j 0; j sizeOfScalingList; j) {if (nextScale ! 0) {// delta_scale se(v)if (!bit_buffer-ReadSignedExponentialGolomb(delta_scale)) {return false;}nextScale (lastScale (delta_scale) 256) % 256;// make sure vector has ith elementwhile (useDefaultScalingMatrixFlag.size() i) {useDefaultScalingMatrixFlag.push_back(0);}useDefaultScalingMatrixFlag[i] (j 0 nextScale 0);}// make sure vector has jth elementwhile (scalingList.size() j) {scalingList.push_back(0);}scalingList[j] (nextScale 0) ? lastScale : nextScale;lastScale scalingList[j];}return true;}// Unpack RBSP and parse SPS state from the supplied buffer.std::shared_ptrH264SpsParser::SpsState H264SpsParser::ParseSps(const uint8_t *data, size_t length) noexcept {std::vectoruint8_t unpacked_buffer UnescapeRbsp(data, length);rtc264::BitBuffer bit_buffer(unpacked_buffer.data(), unpacked_buffer.size());return ParseSps(bit_buffer);}std::shared_ptrH264SpsParser::SpsState H264SpsParser::ParseSps(rtc264::BitBuffer *bit_buffer) noexcept {// H264 SPS Nal Unit (seq_parameter_set_rbsp(()) parser.// Section 7.3.2.1 (Sequence parameter set RBSP syntax) of the H.264// standard for a complete description.auto sps std::make_sharedSpsState();// seq_parameter_set_data()sps-sps_data H264SpsDataParser::ParseSpsData(bit_buffer);if (sps-sps_data nullptr) {return nullptr;}rbsp_trailing_bits(bit_buffer);return sps;}uint32_t H264SpsParser::SpsState::getChromaArrayType() noexcept {return sps_data-getChromaArrayType();}#ifdef FDUMP_DEFINEvoid H264SpsDataParser::SpsDataState::fdump(FILE* outfp,int indent_level) const {fprintf(outfp, sps_data {);indent_level indent_level_incr(indent_level);fdump_indent_level(outfp, indent_level);fprintf(outfp, profile_idc: %i, profile_idc);fdump_indent_level(outfp, indent_level);fprintf(outfp, constraint_set0_flag: %i, constraint_set0_flag);fdump_indent_level(outfp, indent_level);fprintf(outfp, constraint_set1_flag: %i, constraint_set1_flag);fdump_indent_level(outfp, indent_level);fprintf(outfp, constraint_set2_flag: %i, constraint_set2_flag);fdump_indent_level(outfp, indent_level);fprintf(outfp, constraint_set3_flag: %i, constraint_set3_flag);fdump_indent_level(outfp, indent_level);fprintf(outfp, constraint_set4_flag: %i, constraint_set4_flag);fdump_indent_level(outfp, indent_level);fprintf(outfp, constraint_set5_flag: %i, constraint_set5_flag);fdump_indent_level(outfp, indent_level);fprintf(outfp, reserved_zero_2bits: %i, reserved_zero_2bits);fdump_indent_level(outfp, indent_level);fprintf(outfp, level_idc: %i, level_idc);fdump_indent_level(outfp, indent_level);fprintf(outfp, seq_parameter_set_id: %i, seq_parameter_set_id);if (profile_idc 100 || profile_idc 110 || profile_idc 122 ||profile_idc 244 || profile_idc 44 || profile_idc 83 ||profile_idc 86 || profile_idc 118 || profile_idc 128 ||profile_idc 138 || profile_idc 139 || profile_idc 134 ||profile_idc 135) {fdump_indent_level(outfp, indent_level);fprintf(outfp, chroma_format_idc: %i, chroma_format_idc);if (chroma_format_idc 3) {fdump_indent_level(outfp, indent_level);fprintf(outfp, separate_colour_plane_flag: %i,separate_colour_plane_flag);}fdump_indent_level(outfp, indent_level);fprintf(outfp, bit_depth_luma_minus8: %i, bit_depth_luma_minus8);fdump_indent_level(outfp, indent_level);fprintf(outfp, bit_depth_chroma_minus8: %i, bit_depth_chroma_minus8);fdump_indent_level(outfp, indent_level);fprintf(outfp, qpprime_y_zero_transform_bypass_flag: %i,qpprime_y_zero_transform_bypass_flag);fdump_indent_level(outfp, indent_level);fprintf(outfp, seq_scaling_matrix_present_flag: %i,seq_scaling_matrix_present_flag);fdump_indent_level(outfp, indent_level);fprintf(outfp, seq_scaling_list_present_flag {);for (const uint32_t v : seq_scaling_list_present_flag) {fprintf(outfp, %i, v);}fprintf(outfp, });fdump_indent_level(outfp, indent_level);fprintf(outfp, ScalingList4x4 {);for (const uint32_t v : ScalingList4x4) {fprintf(outfp, %i, v);}fprintf(outfp, });fdump_indent_level(outfp, indent_level);fprintf(outfp, UseDefaultScalingMatrix4x4Flag {);for (const uint32_t v : UseDefaultScalingMatrix4x4Flag) {fprintf(outfp, %i, v);}fprintf(outfp, });fdump_indent_level(outfp, indent_level);fprintf(outfp, ScalingList8x8 {);for (const uint32_t v : ScalingList8x8) {fprintf(outfp, %i, v);}fprintf(outfp, });fdump_indent_level(outfp, indent_level);fprintf(outfp, UseDefaultScalingMatrix8x8Flag {);for (const uint32_t v : UseDefaultScalingMatrix8x8Flag) {fprintf(outfp, %i, v);}fprintf(outfp, });fdump_indent_level(outfp, indent_level);fprintf(outfp, delta_scale: %i, delta_scale);}fdump_indent_level(outfp, indent_level);fprintf(outfp, log2_max_frame_num_minus4: %i, log2_max_frame_num_minus4);fdump_indent_level(outfp, indent_level);fprintf(outfp, pic_order_cnt_type: %i, pic_order_cnt_type);if (pic_order_cnt_type 0) {fdump_indent_level(outfp, indent_level);fprintf(outfp, log2_max_pic_order_cnt_lsb_minus4: %i,log2_max_pic_order_cnt_lsb_minus4);} else if (pic_order_cnt_type 1) {fdump_indent_level(outfp, indent_level);fprintf(outfp, delta_pic_order_always_zero_flag: %i,delta_pic_order_always_zero_flag);fdump_indent_level(outfp, indent_level);fprintf(outfp, offset_for_non_ref_pic: %i, offset_for_non_ref_pic);fdump_indent_level(outfp, indent_level);fprintf(outfp, offset_for_top_to_bottom_field: %i,offset_for_top_to_bottom_field);fdump_indent_level(outfp, indent_level);fprintf(outfp, num_ref_frames_in_pic_order_cnt_cycle: %i,num_ref_frames_in_pic_order_cnt_cycle);fdump_indent_level(outfp, indent_level);fprintf(outfp, offset_for_ref_frame {);for (const int32_t v : offset_for_ref_frame) {fprintf(outfp, %i, v);}fprintf(outfp, });}fdump_indent_level(outfp, indent_level);fprintf(outfp, max_num_ref_frames: %i, max_num_ref_frames);fdump_indent_level(outfp, indent_level);fprintf(outfp, gaps_in_frame_num_value_allowed_flag: %i,gaps_in_frame_num_value_allowed_flag);fdump_indent_level(outfp, indent_level);fprintf(outfp, pic_width_in_mbs_minus1: %i, pic_width_in_mbs_minus1);fdump_indent_level(outfp, indent_level);fprintf(outfp, pic_height_in_map_units_minus1: %i,pic_height_in_map_units_minus1);fdump_indent_level(outfp, indent_level);fprintf(outfp, frame_mbs_only_flag: %i, frame_mbs_only_flag);if (!frame_mbs_only_flag) {fdump_indent_level(outfp, indent_level);fprintf(outfp, mb_adaptive_frame_field_flag: %i,mb_adaptive_frame_field_flag);}fdump_indent_level(outfp, indent_level);fprintf(outfp, direct_8x8_inference_flag: %i, direct_8x8_inference_flag);fdump_indent_level(outfp, indent_level);fprintf(outfp, frame_cropping_flag: %i, frame_cropping_flag);if (frame_cropping_flag) {fdump_indent_level(outfp, indent_level);fprintf(outfp, frame_crop_left_offset: %i, frame_crop_left_offset);fdump_indent_level(outfp, indent_level);fprintf(outfp, frame_crop_right_offset: %i, frame_crop_right_offset);fdump_indent_level(outfp, indent_level);fprintf(outfp, frame_crop_top_offset: %i, frame_crop_top_offset);fdump_indent_level(outfp, indent_level);fprintf(outfp, frame_crop_bottom_offset: %i, frame_crop_bottom_offset);}fdump_indent_level(outfp, indent_level);fprintf(outfp, vui_parameters_present_flag: %i,vui_parameters_present_flag);if (vui_parameters_present_flag) {fdump_indent_level(outfp, indent_level);vui_parameters-fdump(outfp, indent_level);}indent_level indent_level_decr(indent_level);fdump_indent_level(outfp, indent_level);fprintf(outfp, });}void H264SpsParser::SpsState::fdump(FILE* outfp, int indent_level) const {fprintf(outfp, sps {);indent_level indent_level_incr(indent_level);fdump_indent_level(outfp, indent_level);sps_data-fdump(outfp, indent_level);indent_level indent_level_decr(indent_level);fdump_indent_level(outfp, indent_level);fprintf(outfp, });}#endif // FDUMP_DEFINE} // namespace h264nalh264_pps_parser.cc 对应的语句语义为第三节中的图像参数集语义代码如下
/** Copyright (c) Facebook, Inc. and its affiliates.*/#include ../h264include/h264nal/h264_pps_parser.h#include stdio.h#include cmath
#include cstdint
#include memory
#include vector#include ../h264include/h264nal/h264_common.hnamespace h264nal {// General note: this is based off the 2012 version of the H.264 standard.
// You can find it on this page:
// http://www.itu.int/rec/T-REC-H.264// Unpack RBSP and parse PPS state from the supplied buffer.std::shared_ptrH264PpsParser::PpsState H264PpsParser::ParsePps(const uint8_t *data, size_t length, uint32_t chroma_format_idc) noexcept {std::vectoruint8_t unpacked_buffer UnescapeRbsp(data, length);rtc264::BitBuffer bit_buffer(unpacked_buffer.data(), unpacked_buffer.size());return ParsePps(bit_buffer, chroma_format_idc);}std::shared_ptrH264PpsParser::PpsState H264PpsParser::ParsePps(rtc264::BitBuffer *bit_buffer, uint32_t chroma_format_idc) noexcept {uint32_t bits_tmp;uint32_t golomb_tmp;// H264 PPS NAL Unit (pic_parameter_set_rbsp()) parser.// Section 7.3.2.2 (Picture parameter set RBSP syntax) of the H.264// standard for a complete description.auto pps std::make_sharedPpsState();// input parameterspps-chroma_format_idc chroma_format_idc;// pic_parameter_set_id ue(v)if (!bit_buffer-ReadExponentialGolomb((pps-pic_parameter_set_id))) {return nullptr;}// seq_parameter_set_id ue(v)if (!bit_buffer-ReadExponentialGolomb((pps-seq_parameter_set_id))) {return nullptr;}// entropy_coding_mode_flag u(1)if (!bit_buffer-ReadBits((pps-entropy_coding_mode_flag), 1)) {return nullptr;}// bottom_field_pic_order_in_frame_present_flag u(1)if (!bit_buffer-ReadBits((pps-bottom_field_pic_order_in_frame_present_flag), 1)) {return nullptr;}// num_slice_groups_minus1 ue(v)if (!bit_buffer-ReadExponentialGolomb((pps-num_slice_groups_minus1))) {return nullptr;}if (pps-num_slice_groups_minus1 0) {// slice_group_map_type ue(v)if (!bit_buffer-ReadExponentialGolomb((pps-slice_group_map_type))) {return nullptr;}if (pps-slice_group_map_type 0) {for (uint32_t iGroup 0; iGroup pps-num_slice_groups_minus1;iGroup) {// run_length_minus1[iGroup] ue(v)if (!bit_buffer-ReadExponentialGolomb(golomb_tmp)) {return nullptr;}pps-run_length_minus1.push_back(golomb_tmp);}} else if (pps-slice_group_map_type 2) {for (uint32_t iGroup 0; iGroup pps-num_slice_groups_minus1;iGroup) {// top_left[iGroup] ue(v)if (!bit_buffer-ReadExponentialGolomb(golomb_tmp)) {return nullptr;}pps-top_left.push_back(golomb_tmp);// bottom_right[iGroup] ue(v)if (!bit_buffer-ReadExponentialGolomb(golomb_tmp)) {return nullptr;}pps-bottom_right.push_back(golomb_tmp);}} else if ((pps-slice_group_map_type 3) ||(pps-slice_group_map_type 4) ||(pps-slice_group_map_type 5)) {// slice_group_change_direction_flag u(1)if (!bit_buffer-ReadBits((pps-slice_group_change_direction_flag), 1)) {return nullptr;}// slice_group_change_rate_minus1 ue(v)if (!bit_buffer-ReadExponentialGolomb((pps-slice_group_change_rate_minus1))) {return nullptr;}} else if (pps-slice_group_map_type 6) {// pic_size_in_map_units_minus1 ue(v)if (!bit_buffer-ReadExponentialGolomb((pps-pic_size_in_map_units_minus1))) {return nullptr;}// slice_group_id u(v)uint32_t slice_group_id_len pps-getSliceGroupIdLen();if (!bit_buffer-ReadBits(bits_tmp, slice_group_id_len)) {return nullptr;}pps-slice_group_id.push_back(bits_tmp);}}// num_ref_idx_l0_active_minus1 ue(v)if (!bit_buffer-ReadExponentialGolomb((pps-num_ref_idx_l0_active_minus1))) {return nullptr;}// num_ref_idx_l1_active_minus1 ue(v)if (!bit_buffer-ReadExponentialGolomb((pps-num_ref_idx_l1_active_minus1))) {return nullptr;}// weighted_pred_flag u(1)if (!bit_buffer-ReadBits((pps-weighted_pred_flag), 1)) {return nullptr;}// weighted_bipred_idc u(2)if (!bit_buffer-ReadBits((pps-weighted_bipred_idc), 2)) {return nullptr;}// pic_init_qp_minus26 se(v)if (!bit_buffer-ReadSignedExponentialGolomb((pps-pic_init_qp_minus26))) {return nullptr;}// pic_init_qs_minus26 se(v)if (!bit_buffer-ReadSignedExponentialGolomb((pps-pic_init_qs_minus26))) {return nullptr;}// chroma_qp_index_offset se(v)if (!bit_buffer-ReadSignedExponentialGolomb((pps-chroma_qp_index_offset))) {return nullptr;}// deblocking_filter_control_present_flag u(1)if (!bit_buffer-ReadBits((pps-deblocking_filter_control_present_flag),1)) {return nullptr;}// constrained_intra_pred_flag u(1)if (!bit_buffer-ReadBits((pps-constrained_intra_pred_flag), 1)) {return nullptr;}// redundant_pic_cnt_present_flag u(1)if (!bit_buffer-ReadBits((pps-redundant_pic_cnt_present_flag), 1)) {return nullptr;}if (more_rbsp_data(bit_buffer)) {// transform_8x8_mode_flag u(1)if (!bit_buffer-ReadBits((pps-transform_8x8_mode_flag), 1)) {return nullptr;}// pic_scaling_matrix_present_flag u(1)if (!bit_buffer-ReadBits((pps-pic_scaling_matrix_present_flag), 1)) {return nullptr;}if (pps-pic_scaling_matrix_present_flag) {uint32_t max_pic_scaling_list_present_flag 6 ((chroma_format_idc ! 3) ? 2 : 6) * pps-transform_8x8_mode_flag;for (uint32_t i 0; i max_pic_scaling_list_present_flag; i) {// pic_scaling_list_present_flag u(1)if (!bit_buffer-ReadBits(bits_tmp, 1)) {return nullptr;}pps-pic_scaling_list_present_flag.push_back(bits_tmp);if (pps-pic_scaling_list_present_flag[i]) {// scaling_list()if (i 6) {(void) pps-scaling_list(bit_buffer, i, pps-ScalingList4x4, 16,pps-UseDefaultScalingMatrix4x4Flag);} else {(void) pps-scaling_list(bit_buffer, i - 6, pps-ScalingList8x8, 64,pps-UseDefaultScalingMatrix4x4Flag);}}}}// second_chroma_qp_index_offset se(v)if (!bit_buffer-ReadSignedExponentialGolomb((pps-second_chroma_qp_index_offset))) {return nullptr;}}rbsp_trailing_bits(bit_buffer);return pps;}uint32_t H264PpsParser::PpsState::getSliceGroupIdLen() noexcept {// Rec. ITU-T H.264 (2012) Page 70, Section 7.4.2.2// slice_group_id[i] identifies a slice group of the i-th slice group// map unit in raster scan order. The size of the slice_group_id[i]// syntax element is Ceil(Log2(num_slice_groups_minus1 1)) bits.// The value of slice_group_id[i] shall be in the range of 0 to// num_slice_groups_minus1, inclusive.return static_castuint32_t(std::ceil(std::log2(1.0 * num_slice_groups_minus1 1)));}// Section 7.3.2.1.1.1bool H264PpsParser::PpsState::scaling_list(rtc264::BitBuffer *bit_buffer, uint32_t i, std::vectoruint32_t scalingList,uint32_t sizeOfScalingList,std::vectoruint32_t useDefaultScalingMatrixFlag) noexcept {uint32_t lastScale 8;uint32_t nextScale 8;for (uint32_t j 0; j sizeOfScalingList; j) {if (nextScale ! 0) {// delta_scale se(v)if (!bit_buffer-ReadSignedExponentialGolomb(delta_scale)) {return false;}nextScale (lastScale (delta_scale) 256) % 256;// make sure vector has ith elementwhile (useDefaultScalingMatrixFlag.size() i) {useDefaultScalingMatrixFlag.push_back(0);}useDefaultScalingMatrixFlag[i] (j 0 nextScale 0);}// make sure vector has jth elementwhile (scalingList.size() j) {scalingList.push_back(0);}scalingList[j] (nextScale 0) ? lastScale : nextScale;lastScale scalingList[j];}return true;}#ifdef FDUMP_DEFINEvoid H264PpsParser::PpsState::fdump(FILE* outfp, int indent_level) const {fprintf(outfp, pps {);indent_level indent_level_incr(indent_level);fdump_indent_level(outfp, indent_level);fprintf(outfp, pic_parameter_set_id: %i, pic_parameter_set_id);fdump_indent_level(outfp, indent_level);fprintf(outfp, seq_parameter_set_id: %i, seq_parameter_set_id);fdump_indent_level(outfp, indent_level);fprintf(outfp, entropy_coding_mode_flag: %i, entropy_coding_mode_flag);fdump_indent_level(outfp, indent_level);fprintf(outfp, bottom_field_pic_order_in_frame_present_flag: %i,bottom_field_pic_order_in_frame_present_flag);fdump_indent_level(outfp, indent_level);fprintf(outfp, num_slice_groups_minus1: %i, num_slice_groups_minus1);if (num_slice_groups_minus1 0) {fdump_indent_level(outfp, indent_level);fprintf(outfp, slice_group_map_type: %i, slice_group_map_type);if (slice_group_map_type 0) {fdump_indent_level(outfp, indent_level);fprintf(outfp, run_length_minus1 {);for (const uint32_t v : run_length_minus1) {fprintf(outfp, %i, v);}fprintf(outfp, });} else if (slice_group_map_type 2) {fdump_indent_level(outfp, indent_level);fprintf(outfp, top_left {);for (const uint32_t v : top_left) {fprintf(outfp, %i, v);}fprintf(outfp, });fdump_indent_level(outfp, indent_level);fprintf(outfp, bottom_right {);for (const uint32_t v : bottom_right) {fprintf(outfp, %i, v);}fprintf(outfp, });} else if ((slice_group_map_type 3) || (slice_group_map_type 4) ||(slice_group_map_type 5)) {fdump_indent_level(outfp, indent_level);fprintf(outfp, slice_group_change_direction_flag: %i,slice_group_change_direction_flag);fdump_indent_level(outfp, indent_level);fprintf(outfp, slice_group_change_rate_minus1: %i,slice_group_change_rate_minus1);} else if (slice_group_map_type 6) {fdump_indent_level(outfp, indent_level);fprintf(outfp, pic_size_in_map_units_minus1: %i,pic_size_in_map_units_minus1);fdump_indent_level(outfp, indent_level);fprintf(outfp, slice_group_id {);for (const uint32_t v : slice_group_id) {fprintf(outfp, %i, v);}fprintf(outfp, });}}fdump_indent_level(outfp, indent_level);fprintf(outfp, num_ref_idx_l0_active_minus1: %i,num_ref_idx_l0_active_minus1);fdump_indent_level(outfp, indent_level);fprintf(outfp, num_ref_idx_l1_active_minus1: %i,num_ref_idx_l1_active_minus1);fdump_indent_level(outfp, indent_level);fprintf(outfp, weighted_pred_flag: %i, weighted_pred_flag);fdump_indent_level(outfp, indent_level);fprintf(outfp, weighted_bipred_idc: %i, weighted_bipred_idc);fdump_indent_level(outfp, indent_level);fprintf(outfp, pic_init_qp_minus26: %i, pic_init_qp_minus26);fdump_indent_level(outfp, indent_level);fprintf(outfp, pic_init_qs_minus26: %i, pic_init_qs_minus26);fdump_indent_level(outfp, indent_level);fprintf(outfp, chroma_qp_index_offset: %i, chroma_qp_index_offset);fdump_indent_level(outfp, indent_level);fprintf(outfp, deblocking_filter_control_present_flag: %i,deblocking_filter_control_present_flag);fdump_indent_level(outfp, indent_level);fprintf(outfp, constrained_intra_pred_flag: %i,constrained_intra_pred_flag);fdump_indent_level(outfp, indent_level);fprintf(outfp, redundant_pic_cnt_present_flag: %i,redundant_pic_cnt_present_flag);fdump_indent_level(outfp, indent_level);fprintf(outfp, transform_8x8_mode_flag: %i, transform_8x8_mode_flag);fdump_indent_level(outfp, indent_level);fprintf(outfp, pic_scaling_matrix_present_flag: %i,pic_scaling_matrix_present_flag);fdump_indent_level(outfp, indent_level);fprintf(outfp, pic_scaling_list_present_flag {);for (const uint32_t v : pic_scaling_list_present_flag) {fprintf(outfp, %i, v);}fprintf(outfp, });fdump_indent_level(outfp, indent_level);fprintf(outfp, ScalingList4x4 {);for (const uint32_t v : ScalingList4x4) {fprintf(outfp, %i, v);}fprintf(outfp, });fdump_indent_level(outfp, indent_level);fprintf(outfp, UseDefaultScalingMatrix4x4Flag {);for (const uint32_t v : UseDefaultScalingMatrix4x4Flag) {fprintf(outfp, %i, v);}fprintf(outfp, });fdump_indent_level(outfp, indent_level);fprintf(outfp, ScalingList8x8 {);for (const uint32_t v : ScalingList8x8) {fprintf(outfp, %i, v);}fprintf(outfp, });fdump_indent_level(outfp, indent_level);fprintf(outfp, UseDefaultScalingMatrix8x8Flag {);for (const uint32_t v : UseDefaultScalingMatrix8x8Flag) {fprintf(outfp, %i, v);}fprintf(outfp, });fdump_indent_level(outfp, indent_level);fprintf(outfp, delta_scale: %i, delta_scale);fdump_indent_level(outfp, indent_level);fprintf(outfp, second_chroma_qp_index_offset: %i,second_chroma_qp_index_offset);indent_level indent_level_decr(indent_level);fdump_indent_level(outfp, indent_level);fprintf(outfp, });}
#endif // FDUMP_DEFINE} // namespace h264nalh264_slice_header_parser.cc 对应的语句语义为第三节中的片头语义代码如下
/** Copyright (c) Facebook, Inc. and its affiliates.*/#include ../h264include/h264nal/h264_slice_header_parser.h#define __STDC_FORMAT_MACROS#include inttypes.h
#include stdio.h#include cmath
#include cstdint
#include memory
#include vector#include ../h264include/h264nal/h264_bitstream_parser_state.h
#include ../h264include/h264nal/h264_common.h
#include ../h264include/h264nal/h264_dec_ref_pic_marking_parser.h
#include ../h264include/h264nal/h264_pps_parser.h
#include ../h264include/h264nal/h264_pred_weight_table_parser.h
#include ../h264include/h264nal/h264_ref_pic_list_modification_parser.h
#include ../h264include/h264nal/h264_sps_parser.h
#include ../webrtc/bit_buffer.hnamespace h264nal {// General note: this is based off the 2012 version of the H.264 standard.
// You can find it on this page:
// http://www.itu.int/rec/T-REC-H.264// Unpack RBSP and parse slice header state from the supplied buffer.std::unique_ptrH264SliceHeaderParser::SliceHeaderStateH264SliceHeaderParser::ParseSliceHeader(const uint8_t *data, size_t length, uint32_t nal_ref_idc,uint32_t nal_unit_type,struct H264BitstreamParserState *bitstream_parser_state) noexcept {std::vectoruint8_t unpacked_buffer UnescapeRbsp(data, length);rtc264::BitBuffer bit_buffer(unpacked_buffer.data(), unpacked_buffer.size());return ParseSliceHeader(bit_buffer, nal_ref_idc, nal_unit_type,bitstream_parser_state);}std::unique_ptrH264SliceHeaderParser::SliceHeaderStateH264SliceHeaderParser::ParseSliceHeader(rtc264::BitBuffer *bit_buffer, uint32_t nal_ref_idc, uint32_t nal_unit_type,struct H264BitstreamParserState *bitstream_parser_state) noexcept {int32_t sgolomb_tmp;// H264 slice header (slice_header()) NAL Unit.// Section 7.3.3 (Slice Header syntax) of the H.264// standard for a complete description.auto slice_header std::make_uniqueSliceHeaderState();// input parametersslice_header-nal_ref_idc nal_ref_idc;slice_header-nal_unit_type nal_unit_type;// first_mb_in_slice ue(v)if (!bit_buffer-ReadExponentialGolomb((slice_header-first_mb_in_slice))) {return nullptr;}// slice_type ue(v)if (!bit_buffer-ReadExponentialGolomb((slice_header-slice_type))) {return nullptr;}// pic_parameter_set_id ue(v)if (!bit_buffer-ReadExponentialGolomb((slice_header-pic_parameter_set_id))) {return nullptr;}// get pps_id and sps_id and check their existenceuint32_t pps_id slice_header-pic_parameter_set_id;if (bitstream_parser_state-pps.find(pps_id) bitstream_parser_state-pps.end()) {// non-existent PPS idreturn nullptr;}auto pps bitstream_parser_state-pps[pps_id];uint32_t sps_id pps-seq_parameter_set_id;if (bitstream_parser_state-sps.find(sps_id) bitstream_parser_state-sps.end()) {// non-existent SPS idreturn nullptr;}auto sps bitstream_parser_state-sps[sps_id];auto sps_data sps-sps_data;slice_header-separate_colour_plane_flag sps_data-separate_colour_plane_flag;if (slice_header-separate_colour_plane_flag) {// colour_plane_id u(2)if (!bit_buffer-ReadBits((slice_header-colour_plane_id), 2)) {return nullptr;}}// frame_num u(v)slice_header-log2_max_frame_num_minus4 sps_data-log2_max_frame_num_minus4;uint32_t frame_num_len slice_header-getFrameNumLen(slice_header-log2_max_frame_num_minus4);if (!bit_buffer-ReadBits((slice_header-frame_num), frame_num_len)) {return nullptr;}slice_header-frame_mbs_only_flag sps_data-frame_mbs_only_flag;if (!slice_header-frame_mbs_only_flag) {// field_pic_flag u(1)if (!bit_buffer-ReadBits((slice_header-field_pic_flag), 1)) {return nullptr;}if (slice_header-field_pic_flag) {// bottom_field_flag u(1)if (!bit_buffer-ReadBits((slice_header-bottom_field_flag), 1)) {return nullptr;}}}uint32_t IdrPicFlag ((slice_header-nal_unit_type 5) ? 1 : 0);if (IdrPicFlag) {// idr_pic_id ue(v)if (!bit_buffer-ReadExponentialGolomb((slice_header-idr_pic_id))) {return nullptr;}}slice_header-pic_order_cnt_type sps_data-pic_order_cnt_type;if (slice_header-pic_order_cnt_type 0) {uint32_t log2_max_pic_order_cnt_lsb_minus4 sps_data-log2_max_pic_order_cnt_lsb_minus4;// pic_order_cnt_lsb u(v)uint32_t pic_order_cnt_lsb_len slice_header-getPicOrderCntLsbLen(log2_max_pic_order_cnt_lsb_minus4);if (!bit_buffer-ReadBits((slice_header-pic_order_cnt_lsb),pic_order_cnt_lsb_len)) {return nullptr;}slice_header-bottom_field_pic_order_in_frame_present_flag pps-bottom_field_pic_order_in_frame_present_flag;if (slice_header-bottom_field_pic_order_in_frame_present_flag !slice_header-field_pic_flag) {// delta_pic_order_cnt_bottom se(v)if (!bit_buffer-ReadSignedExponentialGolomb((slice_header-delta_pic_order_cnt_bottom))) {return nullptr;}}}slice_header-delta_pic_order_always_zero_flag sps_data-delta_pic_order_always_zero_flag;if ((slice_header-pic_order_cnt_type 1) (!slice_header-delta_pic_order_always_zero_flag)) {// delta_pic_order_cnt[0] se(v)if (!bit_buffer-ReadSignedExponentialGolomb(sgolomb_tmp)) {return nullptr;}slice_header-delta_pic_order_cnt.push_back(sgolomb_tmp);if (slice_header-bottom_field_pic_order_in_frame_present_flag !slice_header-field_pic_flag) {// delta_pic_order_cnt[1] se(v)if (!bit_buffer-ReadSignedExponentialGolomb(sgolomb_tmp)) {return nullptr;}slice_header-delta_pic_order_cnt.push_back(sgolomb_tmp);}}slice_header-redundant_pic_cnt_present_flag pps-redundant_pic_cnt_present_flag;if (slice_header-redundant_pic_cnt_present_flag) {// redundant_pic_cnt ue(v)if (!bit_buffer-ReadExponentialGolomb((slice_header-redundant_pic_cnt))) {return nullptr;}}if ((slice_header-slice_type SliceType::B) ||(slice_header-slice_type SliceType::B_ALL)) { // slice_type B// direct_spatial_mv_pred_flag u(1)if (!bit_buffer-ReadBits((slice_header-direct_spatial_mv_pred_flag),1)) {return nullptr;}}if ((slice_header-slice_type SliceType::P) ||(slice_header-slice_type SliceType::P_ALL_H264) ||(slice_header-slice_type SliceType::SP) ||(slice_header-slice_type SliceType::SP_ALL) ||(slice_header-slice_type SliceType::B) ||(slice_header-slice_type SliceType::B_ALL)) { // slice_type P || slice_type SP ||// slice_type B// num_ref_idx_active_override_flag u(1)if (!bit_buffer-ReadBits((slice_header-num_ref_idx_active_override_flag),1)) {return nullptr;}if (slice_header-num_ref_idx_active_override_flag) {// num_ref_idx_l0_active_minus1 ue(v)if (!bit_buffer-ReadExponentialGolomb((slice_header-num_ref_idx_l0_active_minus1))) {return nullptr;}if ((slice_header-slice_type SliceType::B) ||(slice_header-slice_type SliceType::B_ALL)) { // slice_type B// num_ref_idx_l1_active_minus1 ue(v)if (!bit_buffer-ReadExponentialGolomb((slice_header-num_ref_idx_l1_active_minus1))) {return nullptr;}}}}if (slice_header-nal_unit_type 20) {// ref_pic_list_mvc_modification()
#ifdef FPRINT_ERRORS// TODO(chemag): add support for ref_pic_list_mvc_modification()fprintf(stderr,error: unimplemented ref_pic_list_mvc_modification in pps\n);
#endif // FPRINT_ERRORS} else {// ref_pic_list_modification(slice_type)slice_header-ref_pic_list_modification H264RefPicListModificationParser::ParseRefPicListModification(bit_buffer, slice_header-slice_type);if (slice_header-ref_pic_list_modification nullptr) {return nullptr;}}slice_header-weighted_pred_flag pps-weighted_pred_flag;slice_header-weighted_bipred_idc pps-weighted_bipred_idc;if ((slice_header-weighted_pred_flag ((slice_header-slice_type SliceType::P) ||(slice_header-slice_type SliceType::P_ALL_H264) ||(slice_header-slice_type SliceType::SP) ||(slice_header-slice_type SliceType::SP_ALL))) ||((slice_header-weighted_bipred_idc 1) ((slice_header-slice_type SliceType::B) ||(slice_header-slice_type SliceType::B_ALL)))) {// pred_weight_table(slice_type, num_ref_idx_l0_active_minus1,// num_ref_idx_l1_active_minus1)uint32_t ChromaArrayType sps_data-getChromaArrayType();slice_header-pred_weight_table H264PredWeightTableParser::ParsePredWeightTable(bit_buffer, ChromaArrayType, slice_header-slice_type,slice_header-num_ref_idx_l0_active_minus1,slice_header-num_ref_idx_l1_active_minus1);if (slice_header-pred_weight_table nullptr) {return nullptr;}}if (slice_header-nal_ref_idc ! 0) {// dec_ref_pic_marking(nal_unit_type)slice_header-dec_ref_pic_marking H264DecRefPicMarkingParser::ParseDecRefPicMarking(bit_buffer, slice_header-nal_unit_type);if (slice_header-dec_ref_pic_marking nullptr) {return nullptr;}}slice_header-entropy_coding_mode_flag pps-entropy_coding_mode_flag;if (slice_header-entropy_coding_mode_flag (slice_header-slice_type ! SliceType::I) (slice_header-slice_type ! SliceType::I_ALL) (slice_header-slice_type ! SliceType::SI) (slice_header-slice_type ! SliceType::SI_ALL)) {// cabac_init_idc ue(v)if (!bit_buffer-ReadExponentialGolomb((slice_header-cabac_init_idc))) {return nullptr;}}// slice_qp_delta se(v)if (!bit_buffer-ReadSignedExponentialGolomb((slice_header-slice_qp_delta))) {return nullptr;}if ((slice_header-slice_type SliceType::SP) ||(slice_header-slice_type SliceType::SP_ALL) ||(slice_header-slice_type SliceType::SI) ||(slice_header-slice_type SliceType::SI_ALL)) {if ((slice_header-slice_type SliceType::SP) ||(slice_header-slice_type SliceType::SP_ALL)) {// sp_for_switch_flag u(1)if (!bit_buffer-ReadBits((slice_header-sp_for_switch_flag), 1)) {return nullptr;}}// slice_qs_delta se(v)if (!bit_buffer-ReadSignedExponentialGolomb((slice_header-slice_qs_delta))) {return nullptr;}}slice_header-deblocking_filter_control_present_flag pps-deblocking_filter_control_present_flag;if (slice_header-deblocking_filter_control_present_flag) {// disable_deblocking_filter_idc ue(v)if (!bit_buffer-ReadExponentialGolomb((slice_header-disable_deblocking_filter_idc))) {return nullptr;}if (slice_header-disable_deblocking_filter_idc ! 1) {// slice_alpha_c0_offset_div2 se(v)if (!bit_buffer-ReadSignedExponentialGolomb((slice_header-slice_alpha_c0_offset_div2))) {return nullptr;}// slice_beta_offset_div2 se(v)if (!bit_buffer-ReadSignedExponentialGolomb((slice_header-slice_beta_offset_div2))) {return nullptr;}}}slice_header-num_slice_groups_minus1 pps-num_slice_groups_minus1;slice_header-slice_group_map_type pps-slice_group_map_type;if ((slice_header-num_slice_groups_minus1 0) (slice_header-slice_group_map_type 3) (slice_header-slice_group_map_type 5)) {// slice_group_change_cycle u(v)slice_header-pic_width_in_mbs_minus1 sps_data-pic_width_in_mbs_minus1;slice_header-pic_height_in_map_units_minus1 sps_data-pic_height_in_map_units_minus1;slice_header-slice_group_change_rate_minus1 pps-slice_group_change_rate_minus1;uint32_t slice_group_change_cycle_len slice_header-getSliceGroupChangeCycleLen(slice_header-pic_width_in_mbs_minus1,slice_header-pic_height_in_map_units_minus1,slice_header-slice_group_change_rate_minus1);// Rec. ITU-T H.264 (2012) Page 67, Section 7.4.3if (!bit_buffer-ReadBits((slice_header-slice_group_change_cycle),slice_group_change_cycle_len)) {return nullptr;}}return slice_header;}uint32_t H264SliceHeaderParser::SliceHeaderState::getFrameNumLen(uint32_t log2_max_frame_num_minus4) noexcept {// Rec. ITU-T H.264 (2012) Page 62, Section 7.4.3// frame_num is used as an identifier for pictures and shall be// represented by log2_max_frame_num_minus4 4 bits in the bitstream.return log2_max_frame_num_minus4 4;}uint32_t H264SliceHeaderParser::SliceHeaderState::getPicOrderCntLsbLen(uint32_t log2_max_pic_order_cnt_lsb_minus4) noexcept {// Rec. ITU-T H.264 (2012) Page 64, Section 7.4.3// The size of the pic_order_cnt_lsb syntax element is// log2_max_pic_order_cnt_lsb_minus4 4 bits.return log2_max_pic_order_cnt_lsb_minus4 4;}uint32_t H264SliceHeaderParser::SliceHeaderState::getSliceGroupChangeCycleLen(uint32_t pic_width_in_mbs_minus1, uint32_t pic_height_in_map_units_minus1,uint32_t slice_group_change_rate_minus1) noexcept {// Rec. ITU-T H.264 (2012) Page 67, Section 7.4.3// The value of slice_group_change_cycle is represented in the bitstream// by the following number of bits// Ceil(Log2(PicSizeInMapUnits SliceGroupChangeRate 1)) (7-21)uint32_t PicSizeInMapUnits getPicSizeInMapUnits(pic_width_in_mbs_minus1, pic_height_in_map_units_minus1);uint32_t SliceGroupChangeRate getSliceGroupChangeRate(slice_group_change_rate_minus1);return std::ceil(std::log2(1.0 * (PicSizeInMapUnits / SliceGroupChangeRate) 1));}uint32_t H264SliceHeaderParser::SliceHeaderState::getPicWidthInMbs(uint32_t pic_width_in_mbs_minus1) noexcept {return pic_width_in_mbs_minus1 1;}uint32_t H264SliceHeaderParser::SliceHeaderState::getPicHeightInMapUnits(uint32_t pic_height_in_map_units_minus1) noexcept {return pic_height_in_map_units_minus1 1;}uint32_t H264SliceHeaderParser::SliceHeaderState::getPicSizeInMapUnits(uint32_t pic_width_in_mbs_minus1,uint32_t pic_height_in_map_units_minus1) noexcept {uint32_t PicWidthInMbs getPicWidthInMbs(pic_width_in_mbs_minus1);uint32_t PicHeightInMapUnits getPicHeightInMapUnits(pic_height_in_map_units_minus1);return PicWidthInMbs * PicHeightInMapUnits;}uint32_t H264SliceHeaderParser::SliceHeaderState::getSliceGroupChangeRate(uint32_t slice_group_change_rate_minus1) noexcept {return slice_group_change_rate_minus1 1;}#ifdef FDUMP_DEFINEvoid H264SliceHeaderParser::SliceHeaderState::fdump(FILE* outfp,int indent_level) const {fprintf(outfp, slice_header {);indent_level indent_level_incr(indent_level);fdump_indent_level(outfp, indent_level);fprintf(outfp, first_mb_in_slice: %i, first_mb_in_slice);fdump_indent_level(outfp, indent_level);fprintf(outfp, slice_type: %i, slice_type);fdump_indent_level(outfp, indent_level);fprintf(outfp, pic_parameter_set_id: %i, pic_parameter_set_id);if (separate_colour_plane_flag) {fdump_indent_level(outfp, indent_level);fprintf(outfp, colour_plane_id: %i, colour_plane_id);}fdump_indent_level(outfp, indent_level);fprintf(outfp, frame_num: %i, frame_num);if (!frame_mbs_only_flag) {fdump_indent_level(outfp, indent_level);fprintf(outfp, field_pic_flag: %i, field_pic_flag);if (field_pic_flag) {fdump_indent_level(outfp, indent_level);fprintf(outfp, bottom_field_flag: %i, bottom_field_flag);}}if (nal_unit_type 5) {fdump_indent_level(outfp, indent_level);fprintf(outfp, idr_pic_id: %i, idr_pic_id);}if (pic_order_cnt_type 0) {fdump_indent_level(outfp, indent_level);fprintf(outfp, pic_order_cnt_lsb: %i, pic_order_cnt_lsb);if (bottom_field_pic_order_in_frame_present_flag !field_pic_flag) {fdump_indent_level(outfp, indent_level);fprintf(outfp, delta_pic_order_cnt_bottom: %i,delta_pic_order_cnt_bottom);}}if (pic_order_cnt_type 1 !delta_pic_order_always_zero_flag) {fdump_indent_level(outfp, indent_level);fprintf(outfp, delta_pic_order_cnt {);for (const int32_t v : delta_pic_order_cnt) {fprintf(outfp, %i, v);}fprintf(outfp, });}if (redundant_pic_cnt_present_flag) {fdump_indent_level(outfp, indent_level);fprintf(outfp, redundant_pic_cnt: %i, redundant_pic_cnt);}if ((slice_type SliceType::B) ||(slice_type SliceType::B_ALL)) { // slice_type Bfdump_indent_level(outfp, indent_level);fprintf(outfp, direct_spatial_mv_pred_flag: %i,direct_spatial_mv_pred_flag);}if ((slice_type SliceType::P) || (slice_type SliceType::P_ALL) ||(slice_type SliceType::SP) || (slice_type SliceType::SP_ALL) ||(slice_type SliceType::B) ||(slice_type SliceType::B_ALL)) { // slice_type P || slice_type // SP || slice_type Bfdump_indent_level(outfp, indent_level);fprintf(outfp, num_ref_idx_active_override_flag: %i,num_ref_idx_active_override_flag);if (num_ref_idx_active_override_flag) {fdump_indent_level(outfp, indent_level);fprintf(outfp, num_ref_idx_l0_active_minus1: %i,num_ref_idx_l0_active_minus1);if ((slice_type SliceType::B) ||(slice_type SliceType::B_ALL)) { // slice_type Bfdump_indent_level(outfp, indent_level);fprintf(outfp, num_ref_idx_l1_active_minus1: %i,num_ref_idx_l1_active_minus1);}}}fdump_indent_level(outfp, indent_level);ref_pic_list_modification-fdump(outfp, indent_level);if ((weighted_pred_flag ((slice_type SliceType::P) || (slice_type SliceType::P_ALL) ||(slice_type SliceType::SP) || (slice_type SliceType::SP_ALL))) ||((weighted_bipred_idc 1) ((slice_type SliceType::B) || (slice_type SliceType::B_ALL)))) {fdump_indent_level(outfp, indent_level);pred_weight_table-fdump(outfp, indent_level);}if (nal_ref_idc ! 0) {fdump_indent_level(outfp, indent_level);dec_ref_pic_marking-fdump(outfp, indent_level);}if (entropy_coding_mode_flag (slice_type ! SliceType::I) (slice_type ! SliceType::I_ALL) (slice_type ! SliceType::SI) (slice_type ! SliceType::SI_ALL)) {fdump_indent_level(outfp, indent_level);fprintf(outfp, cabac_init_idc: %i, cabac_init_idc);}fdump_indent_level(outfp, indent_level);fprintf(outfp, slice_qp_delta: %i, slice_qp_delta);if ((slice_type SliceType::SP) || (slice_type SliceType::SP_ALL) ||(slice_type SliceType::SI) || (slice_type SliceType::SI_ALL)) {if ((slice_type SliceType::SP) || (slice_type SliceType::SP_ALL)) {fdump_indent_level(outfp, indent_level);fprintf(outfp, sp_for_switch_flag: %i, sp_for_switch_flag);}fdump_indent_level(outfp, indent_level);fprintf(outfp, slice_qs_delta: %i, slice_qs_delta);}if (deblocking_filter_control_present_flag) {fdump_indent_level(outfp, indent_level);fprintf(outfp, disable_deblocking_filter_idc: %i,disable_deblocking_filter_idc);if (disable_deblocking_filter_idc ! 1) {fdump_indent_level(outfp, indent_level);fprintf(outfp, slice_alpha_c0_offset_div2: %i,slice_alpha_c0_offset_div2);fdump_indent_level(outfp, indent_level);fprintf(outfp, slice_beta_offset_div2: %i, slice_beta_offset_div2);}}if ((num_slice_groups_minus1 0) (slice_group_map_type 3) (slice_group_map_type 5)) {fdump_indent_level(outfp, indent_level);fprintf(outfp, slice_group_change_cycle: %i, slice_group_change_cycle);}indent_level indent_level_decr(indent_level);fdump_indent_level(outfp, indent_level);fprintf(outfp, });}
#endif // FDUMP_DEFINE} // namespace h264nalh264_ref_pic_list_modification_parser.cc: 对应的语句语义为第三节中的参考图像序列重排序的语义代码如下
/** Copyright (c) Facebook, Inc. and its affiliates.*/#include ../h264include/h264nal/h264_ref_pic_list_modification_parser.h#define __STDC_FORMAT_MACROS#include inttypes.h
#include stdio.h#include cstdint
#include memory
#include vector#include ../h264include/h264nal/h264_common.hnamespace h264nal {// General note: this is based off the 2012 version of the H.264 standard.
// You can find it on this page:
// http://www.itu.int/rec/T-REC-H.264// Unpack RBSP and parse ref_pic_list_modification state from the supplied
// buffer.std::unique_ptrH264RefPicListModificationParser::RefPicListModificationStateH264RefPicListModificationParser::ParseRefPicListModification(const uint8_t *data, size_t length, uint32_t slice_type) noexcept {std::vectoruint8_t unpacked_buffer UnescapeRbsp(data, length);rtc264::BitBuffer bit_buffer(unpacked_buffer.data(), unpacked_buffer.size());return ParseRefPicListModification(bit_buffer, slice_type);}std::unique_ptrH264RefPicListModificationParser::RefPicListModificationStateH264RefPicListModificationParser::ParseRefPicListModification(rtc264::BitBuffer *bit_buffer, uint32_t slice_type) noexcept {uint32_t golomb_tmp;// H264 ref_pic_list_modification() NAL Unit.// Section 7.3.3.1 (Reference picture list modification syntax) of the// H.264 standard for a complete description.auto ref_pic_list_modification std::make_uniqueRefPicListModificationState();// store input valuesref_pic_list_modification-slice_type slice_type;if ((slice_type ! SliceType::I) (slice_type ! SliceType::I_ALL) (slice_type ! SliceType::SI) (slice_type !SliceType::SI_ALL)) { // slice_type ! I slice_type ! SI// ref_pic_list_modification_flag_l0 u(1)if (!bit_buffer-ReadBits((ref_pic_list_modification-ref_pic_list_modification_flag_l0),1)) {return nullptr;}if (ref_pic_list_modification-ref_pic_list_modification_flag_l0) {do {// modification_of_pic_nums_idc[i] ue(v)if (!bit_buffer-ReadExponentialGolomb(golomb_tmp)) {return nullptr;}ref_pic_list_modification-modification_of_pic_nums_idc.push_back(golomb_tmp);if ((ref_pic_list_modification-modification_of_pic_nums_idc.back() 0) ||(ref_pic_list_modification-modification_of_pic_nums_idc.back() 1)) {// abs_diff_pic_num_minus1[i] ue(v)if (!bit_buffer-ReadExponentialGolomb(golomb_tmp)) {return nullptr;}ref_pic_list_modification-abs_diff_pic_num_minus1.push_back(golomb_tmp);} else if (ref_pic_list_modification-modification_of_pic_nums_idc.back() 2) {// long_term_pic_num[i] ue(v)if (!bit_buffer-ReadExponentialGolomb(golomb_tmp)) {return nullptr;}ref_pic_list_modification-long_term_pic_num.push_back(golomb_tmp);}} while (ref_pic_list_modification-modification_of_pic_nums_idc.back() !3);}}if ((slice_type SliceType::B) ||(slice_type SliceType::B_ALL)) { // slice_type B// ref_pic_list_modification_flag_l1 u(1)if (!bit_buffer-ReadBits((ref_pic_list_modification-ref_pic_list_modification_flag_l1),1)) {return nullptr;}if (ref_pic_list_modification-ref_pic_list_modification_flag_l1) {do {// modification_of_pic_nums_idc[i] ue(v)if (!bit_buffer-ReadExponentialGolomb(golomb_tmp)) {return nullptr;}ref_pic_list_modification-modification_of_pic_nums_idc.push_back(golomb_tmp);if ((ref_pic_list_modification-modification_of_pic_nums_idc.back() 0) ||(ref_pic_list_modification-modification_of_pic_nums_idc.back() 1)) {// abs_diff_pic_num_minus1[i] ue(v)if (!bit_buffer-ReadExponentialGolomb(golomb_tmp)) {return nullptr;}ref_pic_list_modification-abs_diff_pic_num_minus1.push_back(golomb_tmp);} else if (ref_pic_list_modification-modification_of_pic_nums_idc.back() 2) {// long_term_pic_num[i] ue(v)if (!bit_buffer-ReadExponentialGolomb(golomb_tmp)) {return nullptr;}ref_pic_list_modification-long_term_pic_num.push_back(golomb_tmp);}} while (ref_pic_list_modification-modification_of_pic_nums_idc.back() !3);}}return ref_pic_list_modification;}#ifdef FDUMP_DEFINEvoid H264RefPicListModificationParser::RefPicListModificationState::fdump(FILE* outfp, int indent_level) const {fprintf(outfp, ref_pic_list_modification {);indent_level indent_level_incr(indent_level);fdump_indent_level(outfp, indent_level);fprintf(outfp, ref_pic_list_modification_flag_l0: %i,ref_pic_list_modification_flag_l0);fdump_indent_level(outfp, indent_level);fprintf(outfp, ref_pic_list_modification_flag_l1: %i,ref_pic_list_modification_flag_l1);if (modification_of_pic_nums_idc.size() 0) {fdump_indent_level(outfp, indent_level);fprintf(outfp, modification_of_pic_nums_idc {);for (const uint32_t v : modification_of_pic_nums_idc) {fprintf(outfp, %i, v);}fprintf(outfp, });}if (abs_diff_pic_num_minus1.size() 0) {fdump_indent_level(outfp, indent_level);fprintf(outfp, abs_diff_pic_num_minus1 {);for (const uint32_t v : abs_diff_pic_num_minus1) {fprintf(outfp, %i, v);}fprintf(outfp, });}if (long_term_pic_num.size() 0) {fdump_indent_level(outfp, indent_level);fprintf(outfp, long_term_pic_num {);for (const uint32_t v : long_term_pic_num) {fprintf(outfp, %i, v);}fprintf(outfp, });}indent_level indent_level_decr(indent_level);fdump_indent_level(outfp, indent_level);fprintf(outfp, });}
#endif // FDUMP_DEFINE} // namespace h264nalh264_pred_weight_table_parser.cc 对应的语句语义为第三节中的加权预测的语义具体代码可去githubhttps://github.com/wangyongyao1989/WyFFmpeg/tree/main/h264 查看。 h264_dec_ref_pic_marking_parser.cc 对应的语句语义为第三节中的参考图像序列标记Marking操作的语义具体代码可去githubhttps://github.com/wangyongyao1989/WyFFmpeg/tree/main/h264 查看。 如上述代码中的语句语义是根据基于H.264标准的2012版本。 参考资料 《新一代视频压缩编码标准 —H.264/AVC》——毕厚杰 王健 编著 https://github.com/wangyongyao1989/WyFFmpeg/tree/main/h264 H.264 : Advanced video coding for generic audiovisual services