H.264学习笔记之一(层次结构,NAL,SPS)
一 H.264句法
1.1元素分层结构
H.264编码器输出的Bit流中,每个Bit都隶属于某个句法元素。句法元素被组织成有层次的结构,分别描述各个层次的信息。

图1
H.264分层结构由五层组成,分别是序列参数集、图像参数集、片(Slice)、和宏块和子块。参数集是一个独立的数据单位,不依赖于参数集外的其它句法元素。图2描述了参数集与参数集外的句法元素之间的关系。

图2
一个参数集不对应某一个特定的图像或序列,同一序列参数集可以被多个图像参数集引用,同理,同一个图像参数集也可以被多个图像引用。只在编码器认为需要更新参数集的内容时,才会发出新的参数集。
在H.264中,图像以序列为单位进行组织。一个序列的第一个图像叫做IDR图像,IDR图像都是I帧,H.264引入IDR图像为了解码的同步,当解码器解码到IDR图像时,立即将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始一个新的序列。这样,如果前一个序列出现重大错误,在这里可以获得重新同步的机会。IDR图像之后的图像永远不会使用IDR之前的图像的数据来解码。
IDR是I帧,但I帧不一定是IDR。I帧之后的图像有可能会使用I帧之前的图像做运动参考。
1.2描述子
描述子描述从Bit流中取出句法元素的方法。
| 
 编号  | 
 语法  | 
 说明  | 
| 
 1  | 
 ae(e)  | 
 CABAC  | 
| 
 2  | 
 b(8)  | 
 读进连续的8个Bit  | 
| 
 3  | 
 ce(v)  | 
 CAVLC  | 
| 
 4  | 
 f(n)  | 
 读进连续的n个Bit  | 
| 
 5  | 
 i(n)/i(v)  | 
 读进连续的若干Bit,并把它们解释为有符号整数  | 
| 
 6  | 
 me(v)  | 
 映射指数Golomb熵编码  | 
| 
 7  | 
 se(v)  | 
 有符号指数Golomb熵编码  | 
| 
 8  | 
 te(v)  | 
 截断指数Golomb熵编码  | 
| 
 9  | 
 u(n)/u(v)  | 
 读进连续的若干Bit,并把它们解释为无符号整数  | 
| 
 10  | 
 ue(v)  | 
 无符号指数Golomb熵编码  | 
表1
1.3句法的表示方法
句法元素的名称由小写字母和一系列下划线组成,变量名称是大小写字母组成,中间没有下划线。
二 句法表
定义了H.264的句法,指明在码流中依次出现的句法元素及它们出现的条件、提取描述子等。句法表是分层嵌套的。
句法表中的C字段表示该句法元素的分类,这是为片区服务,分类的具体含义如下表描述。
| 
 nal_unit_type  | 
 NAL类型  | 
 C  | 
| 
 0  | 
 未使用  | 
|
| 
 1  | 
 不分区、非IDR的片  | 
 2,3,4  | 
| 
 2  | 
 片分区A  | 
 2  | 
| 
 3  | 
 片分区B  | 
 3  | 
| 
 4  | 
 版分区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
2.1 NAL语法
编码器将每个NAL各自独立、完整地放入一个分组,因为分组都有头部,解码器可以方便地检测出NAL的分界,并依次取出NAL进行解码。
每个NAL前有一个起始码 0x000001,解码器检测每个起始码,作为一个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的长度,然后再开始解码。
| 
 句法  | 
 C  | 
 Desc  | 
| 
 nal_nuit(NumBytesInNALunit){/* NumBytesInNALunit为统计出来的数据长度 */  | 
||
| 
 forbidden_zero_bit /* 等于0 */  | 
 All  | 
 f(1)  | 
| 
 nal_ref_idc/* 当前NAL的优先级,取值范围0-3 */  | 
 All  | 
 u(2)  | 
| 
 nal_unit_type /* NAL类型,见表2描述 */  | 
 All  | 
 u(5)  | 
| 
 NumBytesInRBSP=0  | 
||
| 
 for(i=1;i<NumBytesInNALunit;i++){  | 
||
| 
 if(i+2<NumBytesInNALunit && next_bits(24)==0x000003{  | 
||
| 
 /* 0x000003伪起始码,需要删除0x03这个字节 */  | 
||
| 
 rbsp_byte[NumBytesInRBSP++]  | 
 All  | 
 b(8)  | 
| 
 rbsp_byte[NumBytesInRBSP++]  | 
 All  | 
 b(8)  | 
| 
 i+=2/* 取出前两个0x00后,跳过0x03 */  | 
||
| 
 emulation_prevention_three_byte/* equal to 0x03 */  | 
 All  | 
 f(8)  | 
| 
 }else{  | 
||
| 
 rbsp_byte[NumBytesInRBSP++] /* 继续读取后面的字节 */  | 
 All  | 
 b(8)  | 
| 
 }  | 
||
| 
 }  | 
表3
2.2序列参数集(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  | 
|
| 
 }  | 
表4
H.264学习笔记之一(层次结构,NAL,SPS)的更多相关文章
- 02:H.264学习笔记
		
H.264组成 1.网络提取层 (Network Abstraction Layer,NAL) 2.视讯编码层 (Video Coding Layer,VCL) a.H.264/AVC影像格式阶层架构 ...
 - H.264学习笔记
		
1.帧和场的概念 视频的一场或一帧可用来产生一个编码图像.通常,视频帧可以分成两种类型:连续或隔行视频帧.我们平常看的电视是每秒25帧,即每秒更换25个图像,由于视觉暂留效应,所以人眼不会感到闪烁.每 ...
 - H.264学习笔记5——熵编码之CAVLC
		
H.264中,4x4的像素块经过变换和量化之后,低频信号集中在左上角,大量高频信号集中在右下角.左边的低频信号相对数值较大,而右下角的大量高频信号都被量化成0.1和-1:变换量化后的残差信息有一定的统 ...
 - H.264学习笔记1——相关概念
		
此处记录学习AVC过程中的一些基本概念,不定时更新. frame:帧,相当于一幅图像,包含一个亮度矩阵和两个色度矩阵. field:场,一帧图像,通过隔行扫描得到奇偶两场,分别称为顶场和底场或奇场和偶 ...
 - H.264学习笔记6——指数哥伦布编码
		
一.哥伦布码 哥伦布码就是将编码对象分能成等间隔的若干区间(Group),每个Group有一个索引值:Group Id. >对于Group Id采用二元码编码: >对于Group内的编码对 ...
 - H.264学习笔记4——变换量化
		
A.变换量化过程总体介绍 经过帧内(16x16和4x4亮度.8x8色度)和帧间(4x4~16x16亮度.4x4~8x8色度)像素块预测之后,得到预测块的残差,为了压缩残差信息的统计冗余,需要对残差数据 ...
 - H.264学习笔记3——帧间预测
		
帧间预测主要包括运动估计(运动搜索方法.运动估计准则.亚像素插值和运动矢量估计)和运动补偿. 对于H.264,是对16x16的亮度块和8x8的色度块进行帧间预测编码. A.树状结构分块 H.264的宏 ...
 - H.264学习笔记2——帧内预测
		
帧内预测:根据经过反量化和反变换(没有进行去块效应)之后的同一条带内的块进行预测. A.4x4亮度块预测: 用到的像素和预测方向如图: a~f是4x4块中要预测的像素值,A~Q是临块中解码后的参考值. ...
 - 【视频编解码·学习笔记】5. NAL Unit 结构分析
		
在上篇笔记中通过一个小程序,可以提取NAL Unit所包含的的字节数据.H.264码流中的每一个NAL Unit的作用并不是相同的,而是根据不同的类型起不同的作用.下面将对NAL Unit中的数据进行 ...
 
随机推荐
- BestCoder Round #3
			
Task schedule http://acm.hdu.edu.cn/showproblem.php?pid=4907 #include<cstdio> #include<cstr ...
 - 【WCF--初入江湖】03 配置服务
			
03 配置服务 数据库 生成数据库脚本: CREATE DATABASE [EmployeeDb]; CREATE TABLE [dbo].[T_Employee]( [Id] [,) NOT NUL ...
 - PHP7 扩展之自动化测试
			
在安装 PHP7 及各种扩展的过程中,如果你是用源码安装,会注意到在 make 成功之后总会有一句提示:Don't forget to run 'make test'. 这个 make test 就是 ...
 - java核心技术记录之集合
			
java库中的具体集合: 集合类型 描述 ArrayList 一种可以动态增长和缩减的索引序列 LinkedList 一种可以在任何位置进行高效地插入和删除操作的有序序列 ArrarDeque 一种用 ...
 - 安装SQL Server 2012遇到“需要更新的以前的Visual Studio 2010实例.”
			
Microsoft Visual Studio 2010 Service Pack 1(exe) 下载链接:http://www.microsoft.com/zh-cn/download/confir ...
 - lintcode 中等题:subSets 子集
			
题目 子集 给定一个含不同整数的集合,返回其所有的子集 样例 如果 S = [1,2,3],有如下的解: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], ...
 - AndroidManifest.xml介绍一
			
下面是AndroidManifest.xml的简单介绍,直接上图! 一.manifest结点的属性介绍 二.application结点属性介绍 三.activity.intent-filter.use ...
 - apache配置优化
			
最近参加了很多面试,多多少少有点小感悟,可以说观念转变了不少,特别是对于作为一个开发人员的定位,原来只是认为开发人员就只需要写好代码就行了,所以只需要有数据结构,算法,设计模式,重构方面的知识就行了. ...
 - 枚举桌面应用程序lnk路径并得到对应程序的绝对路径(使用SHGetDesktopFolder函数枚举,然后使用COM查询IShellFolder,IShellLink和IPersistFile接口)
			
// 枚举桌面上应用程序的lnk路径 void EnumDesktopLnkPath() { WCHAR szPath[MAX_PATH] = {0}; SHGetSpecialFolderPathW ...
 - jQuery编程基础精华03(RadioButton操作,事件,鼠标)
			
RadioButton操作 取得RadioButton的选中值,被选中的radio只有一个值,所以直接用val() $('#btn1').click(function () { ...