转载注明出处:http://blog.csdn.net/heanyu/article/details/6190797
这个csdn上写的非常好的对H264参数结构的文章,怕以后找不到了,因此转载了。这里再次感谢原作者。
一、元素的分层结构
H.264编码器输出的Bit流中,每个Bit都隶属于某个句法元素。句法元素被组织成有层次的结构,分别描述各个层次的信息。
在H.264 中,句法元素共被组织成 序列、图像、片、宏块、子宏块五个层次。在这样的结构中,每一层的头部和它的数据部分形成管理与被管理的强依赖关系,头部的句法元素是该层数据的核心,而一旦头部丢失,数据部分的信息几乎不可能再被正确解码出来,尤其在序列层及图像层。
在 H.264 中,分层结构最大的不同是取消了序列层和图像层,并将原本属于序列和图像头部的大部分句法元素游离出来形成序列和图像两级参数集,其余的部分则放入片层。
参数集是一个独立的数据单位,不依赖于参数集外的其他句法元素。一个参数集不对应某一个特定的图像或序列,同一序列参数集可以被多个图像参数集引用,同理,同一个图像参数集也可以被多个图像引用。只在编码器认为需要更新参数集的内容时,才会发出新的参数集。
复杂通信中的码流中可能出现的数据单位:
IDR: 在H.264中,图像以序列为单位进行组织。一个序列的第一个图像叫做 IDR 图像(立即刷新图像),IDR 图像都是 I 帧图像。H.264 引入 IDR 图像是为了解码的重同步,当解码器解码到 IDR 图像时,立即将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始一个新的序列。这样,如果前一个序列出现重大错误,在这里可以获得重新同步的机会。IDR图像之后的图像永远不会使用IDR之前的图像的数据来解码。 IDR 图像一定是 I 图像,但 I 图像不一定是 IDR 图像。I帧之后的图像有可能会使用I帧之前的图像做运动参考。
二、网络提取层NAL (Net Abstraction Layer) & 视频编码层VCL (Video Coding Layer)
H.264 的功能分为两层,即视频编码层(VCL)和网络提取层(NAL,Network Abstraction Layer)。VCL 数据即编码处理的输出,它表示被压缩编码后的视频数据序列。在 VCL 数据传输或存储之前,这些编码的 VCL 数据,先被映射或封装进 NAL 单元中。
每个NAL 单元包括:一组对应于视频编码数据的 NAL 头信息和一个原始字节序列负荷(RBSP)。
头信息中包含着一个可否丢弃的指示 标记,标识着该NAL单元的丢弃能否引起错误扩散,一般,如果NAL单元中的信息不用于构建参考图像,则认为可以将其丢弃;最后包含的是NAL单元的类型 信息,暗示着其内含有效载荷的内容。 送到解码器端的NAL单元必须遵守严格的顺序,如果应用程序接收到的NAL单元处于乱序,则必须提供一种恢复其正确顺序的方法。对于RTP/UDP/IP系统,则可以直接将编码器输出的NAL单元作为RTP的有效载荷。NAL 单元序列的结构如下:
RBSP的类型:
RBSP 类型之一 PS: 包括序列参数集 SPS 和 图像参数集 PPS
SPS 包含的是针对一连续编码视频序列的参数,如标识符 seq_parameter_set_id、帧数及 POC 的约束、参考帧数目、解码图像尺寸和帧场编码模式选择标识等等。
PPS对应的是一个序列中某一幅图像或者某几幅图像,其参数如标识符 pic_parameter_set_id、可选的 seq_parameter_set_id、熵编码模式选择标识、片组数目、初始量化参数和去方块滤波系数调整标识等等。
数据分割:组成片的编码数据存放在 3 个独立的 DP(数据分割,A、B、C)中,各自包含一个编码片的子集。分割A包含片头和片中每个宏块头数据。分割B包含帧内和 SI 片宏块的编码残差数据。分割 C包含帧间宏块的编码残差数据。每个分割可放在独立的 NAL 单元并独立传输。
编码器将每个NAL各自独立、完整地放入一个分组,因为分组都有头部,解码器可以方便地检测出NAL的分界,并依次取出NAL进行解码。每个NAL前有一个起始码 0x00 00 01(或者0x00 00 00 01),解码器检测每个起始码,作为一个NAL的起始标识,当检测到下一个起始码时,当前NAL结束。同时H.264规定,当检测到0x000000时,也可以表征当前NAL的结束。对于NAL中数据出现0x000001或0x000000时,H.264引入了防止竞争机制,如果编码器检测到NAL数据存在0x000001或0x000000时,编码器会在最后个字节前插入一个新的字节0x03,这样:
0x000000->0x00000300
0x000001->0x00000301
0x000002->0x00000302
0x000003->0x00000303
解码器检测到0x000003时,把03抛弃,恢复原始数据。解码器在解码时,首先逐个字节读取NAL的数据,统计NAL的长度,然后再开始解码。
NALU类型
标识NAL单元中的RBSP数据类型,其中,nal_unit_type为1, 2, 3, 4, 5及12的NAL单元称为VCL的NAL单元,其他类型的NAL单元为非VCL的NAL单元。
0:未规定
1:非IDR图像中不采用数据划分的片段
2:非IDR图像中A类数据划分片段
3:非IDR图像中B类数据划分片段
4:非IDR图像中C类数据划分片段
5:IDR图像的片段
6:补充增强信息 (SEI)
7:序列参数集
8:图像参数集
9:分割符
10:序列结束符
11:流结束符
12:填充数据
13 – 23:保留
24 – 31:未规定
NALU的顺序要求
H.264/AVC标准对送到解码器的NAL单元顺序是有严格要求的,如果NAL单元的顺序是混乱的,必须将其重新依照规范组织后送入解码器,否则解码器不能够正确解码。
1.序列参数集NAL单元 必须在传送所有以此参数集为参考的其他NAL单元之前传送,不过允许这些NAL单元中间出现重复的序列参数集NAL单元。 所谓重复的详细解释为:序列参数集NAL单元都有其专门的标识,如果两个序列参数集NAL单元的标识相同,就可以认为后一个只不过是前一个的拷贝,而非新的序列参数集。
2.图像参数集NAL单元 必须在所有以此参数集为参考的其他NAL单元之先,不过允许这些NAL单元中间出现重复的图像参数集NAL单元,这一点与上述的序列参数集NAL单元是相同的。
3.不同基本编码图像中的片段(slice)单元和数据划分片段(data partition)单元在顺序上不可以相互交叉,即不允许属于某一基本编码图像的一系列片段(slice)单元和数据划分片段(data partition)单元中忽然出现另一个基本编码图像的片段(slice)单元片段和数据划分片段(data partition)单元。
4.参考图像的影响:如果一幅图像以另一幅图像为参考,则属于前者的所有片段(slice)单元和数据划分片段(data partition)单元必须在属于后者的片段和数据划分片段之后,无论是基本编码图像还是冗余编码图像都必须遵守这个规则
5.基本编码图像的所有片段(slice)单元和数据划分片段(data partition)单元必须在属于相应冗余编码图像的片段(slice)单元和数据划分片段(data partition)单元之前。
6.如果数据流中出现了连续的无参考基本编码图像,则图像序号小的在前面。
7.如果arbitrary_slice_order_allowed_flag置为1,一个基本编码图像中的片段(slice)单元和数据划分片段(data partition)单元的顺序是任意的,如果arbitrary_slice_order_allowed_flag置为零,则要按照片段中第一个宏块的位置来确定片段的顺序,若使用数据划分,则A类数据划分片段在B类数据划分片段之前,B类数据划分片段在C类数据划分片段之前,而且对应不同片段的数据划分片段不能相互交叉,也不能与没有数据划分的片段相互交叉。
8.如果存在SEI(补充增强信息) 单元的话,它必须在它所对应的基本编码图像的片段(slice)单元和数据划分片段(data partition)单元之前,并同时必须紧接在上一个基本编码图像的所有片段(slice)单元和数据划分片段(data partition)单元后边。假如SEI属于多个基本编码图像,其顺序仅以第一个基本编码图像为参照。
9.如果存在图像分割符的话,它必须在所有SEI 单元、基本编码图像的所有片段slice)单元和数据划分片段(data partition)单元之前,并且紧接着上一个基本编码图像那些NAL单元。
10.如果存在序列结束符,且序列结束符后还有图像,则该图像必须是IDR(即时解码器刷新)图像。序列结束符的位置应当在属于这个IDR图像的分割符、SEI 单元等数据之前,且紧接着前面那些图像的NAL单元。如果序列结束符后没有图像了,那么它的就在比特流中所有图像数据之后。
11.流结束符在比特流中的最后。
三、序列参数集层(SPS) & 图像参数集语义
1: seq_parameter_set_rbsp( ) {
2: // profile_idc level_idc 指明所用 profile、level
3: profile_idc
4: // constraint_set0_flag 等于 1 时表示必须遵从附录 A.2.1 所指明的所有制约条件。等于 0 时表示不必遵从所有条件。
5: constraint_set0_flag
6: // constraint_set1_flag 等于 1 时表示必须遵从附录 A.2.2 所指明的所有制约条件。等于 0 时表示不必遵从所有条件。
7: constraint_set1_flag
8: // constraint_set2_flag 等于 1 时表示必须遵从附录 A.2.3 所指明的所有制约条件。等于 0 时表示不必遵从所有条件。
9: constraint_set2_flag
10: // reserved_zero_5bits 在目前的标准中本句法元素必须等于 0,其他的值保留做将来用,解码器应该忽略本句法元素的值。
11: reserved_zero_5bits /* equal to 0 */
12: level_idc /* 指明所用的Level */
13: seq_parameter_set_id /* 指明本序列参数集的 id 号,这个 id 号将被 picture 参数集引用,本句法元素的值应该在[0,31],,编码需要产生新的序列集时,使用新的id,而不是改变原来参数集的内容 */
14: // log2_max_frame_num_minus4 这个句法元素主要是为读取另一个句法元素 frame_num 服务的,frame_num 是最重要的句法元素之一,它标识所属图像的解码顺序 。这个句法元素同时也指明了 frame_num 的所能达到的最大值: MaxFrameNum = 2*exp( log2_max_frame_num_minus4 + 4 )
15: log2_max_frame_num_minus4
16: // pic_order_cnt_type 指明了 poc (picture order count) 的编码方法,poc 标识图像的播放顺序。由poc 可以由 frame-num 通过映射关系计算得来,也可以索性由编码器显式地传送。
17: pic_order_cnt_type
18: if( pic_order_cnt_type == 0 )
19: // log2_max_pic_order_cnt_lsb_minus4 指明了变量 MaxPicOrderCntLsb 的值: MaxPicOrderCntLsb = pow(2, (log2_max_pic_order_cnt_lsb_minus4 + 4) )
20: log2_max_pic_order_cnt_lsb_minus4
21: else if( pic_order_cnt_type == 1 ) {
22: // delta_pic_order_always_zero_flag 等于 1 时,句法元素 delta_pic_order_cnt[0]和 delta_pic_order_cnt[1]
23: 不在片头出现,并且它们的值默认为 0; 本句法元素等于 0 时,上述的两个句法元素将在片头出现。
24: delta_pic_order_always_zero_flag
25: // offset_for_non_ref_pic 被用来计算非参考帧或场的 POC,本句法元素的值应该在[pow(-2, 31) , pow(2, 31) – 1]。
26: offset_for_non_ref_pic
27: // offset_for_top_to_bottom_field 被用来计算帧的底场的 POC, 本句法元素的值应该在[pow(-2, 31) , pow(2, 31) – 1]。
28: offset_for_top_to_bottom_field
29: // num_ref_frames_in_pic_order_cnt_cycle 被用来解码POC, 本句法元素的值应该在[0,255]。
30: num_ref_frames_in_pic_order_cnt_cycle
31: // offset_for_ref__frame[i] 用于解码 POC,本句法元素对循环num_ref_frames_in_pic_order_cycle 中的每一个元素指定一个偏移。
32: for( i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++ )
33: offset_for_ref_frame[ i ]
34: }
35: // num_ref_frames 指定参考帧队列可能达到的最大长度,解码器依照这个句法元素的值开辟存储区,这个存储区用于存放已解码的参考帧,H.264 规定最多可用 16 个参考帧,本句法元素的值最大为 16。值得注意的是这个长度以帧为单位,如果在场模式下,应该相应地扩展一倍。
36: num_ref_frames
37: // gaps_in_frame_num_value_allowed_flag 这个句法元素等于 1 时,表示允许句法元素 frame_num 可以不连续。当传输信道堵塞严重时,编码器来不及将编码后的图像全部发出,这时允许丢弃若干帧图像。
38: gaps_in_frame_num_value_allowed_flag
39: // pic_width_in_mbs_minus1 本句法元素加 1 后指明图像宽度,以宏块为单位: PicWidthInMbs = pic_width_in_mbs_minus1 + 1 通过这个句法元素解码器可以计算得到亮度分量以像素为单位的图像宽度: PicWidthInSamplesL = PicWidthInMbs * 16
40: pic_width_in_mbs_minus1
41: // pic_height_in_map_units_minus1 本句法元素加 1 后指明图像高度: PicHeightInMapUnits = pic_height_in_map_units_minus1 + 1
42: pic_height_in_map_units_minus1
43: // frame_mbs_only_flag 本句法元素等于 0 时表示本序列中所有图像的编码模式都是帧,没有其他编码模式存在;本句法元素等于 1 时 ,表示本序列中图像的编码模式可能是帧,也可能是场或帧场自适应,某个图像具体是哪一种要由其他句法元素决定。
44: frame_mbs_only_flag
45: // mb_adaptive_frame_field_flag 指明本序列是否属于帧场自适应模式。mb_adaptive_frame_field_flag等于1时表明在本序列中的图像如果不是场模式就是帧场自适应模式,等于0时表示本序列中的图像如果不是场模式就是帧模式。。表 列举了一个序列中可能出现的编码模式:
46: if( !frame_mbs_only_flag )
47: mb_adaptive_frame_field_flag
48: // direct_8x8_inference_flag 用于指明 B 片的直接和 skip 模式下运动矢量的预测方法。
49: direct_8x8_inference_flag
50: // frame_cropping_flag 用于指明解码器是否要将图像裁剪后输出,如果是的话,后面紧跟着的四个句法元素分别指出左右、上下裁剪的宽度。
51: frame_cropping_flag
52: if( frame_cropping_flag ) {
53: frame_crop_left_offset
54: frame_crop_right_offset
55: frame_crop_top_offset
56: frame_crop_bottom_offset
57: }
58: // vui_parameters_present_flag 指明 vui 子结构是否出现在码流中,vui 用以表征视频格式等额外信息。
59: vui_parameters_present_flag
60: if( vui_parameters_present_flag )
61: vui_parameters( )
62: rbsp_trailing_bits( )
63: }
1: pic_parameter_set_rbsp( ) {
2: // pic_parameter_set_id 用以指定本参数集的序号,该序号在各片的片头被引用。
3: pic_parameter_set_id
4: // seq_parameter_set_id 指明本图像参数集所引用的序列参数集的序号。
5: seq_parameter_set_id
6: // entropy_coding_mode_flag 指明熵编码的选择,本句法元素为0时,表示熵编码使用 CAVLC,本句法元素为1时表示熵编码使用 CABAC
7: entropy_coding_mode_flag
8: // pic_order_present_flag POC 的三种计算方法在片层还各需要用一些句法元素作为参数,本句法元素等于1时表示在片头会有句法元素指明这些参数;本句法元素等于0时,表示片头不会给出这些参数,这些参数使用默认值
9: pic_order_present_flag
10: // num_slice_groups_minus1 本句法元素加1后指明图像中片组的个数。H.264 中没有专门的句法元素用于指明是否使用片组模式,当本句法元素等于0(即只有一个片组),表示不使用片组模式,后面也不会跟有用于计算片组映射的句法元素。
11: num_slice_groups_minus1
12: if( num_slice_groups_minus1 > 0 ) {
13: /* slice_group_map_type 用以指明片组分割类型。
14: map_units 的定义:
15: - 当 frame_mbs_only_flag 等于1时,map_units 指的就是宏块
16: - 当 frame_mbs_only_flag 等于0时
17: - 帧场自适应模式时,map_units 指的是宏块对
18: - 场模式时,map_units 指的是宏块
19: - 帧模式时,map_units 指的是与宏块对相类似的,上下两个连续宏块的组合体。 */
20: slice_group_map_type
21: if( slice_group_map_type = = 0 )
22: for( iGroup = 0; iGroup <= num_slice_groups_minus1; iGroup++ )
23: // run_length_minus1[i] 用以指明当片组类型等于0时,每个片组连续的 map_units 个数
24: run_length_minus1[ iGroup ]
25: else if( slice_group_map_type = = 2 )
26: for( iGroup = 0; iGroup < num_slice_groups_minus1; iGroup++ ) {
27: // top_left[i],bottom_right[i] 用以指明当片组类型等于2时,矩形区域的左上及右下位置。
28: top_left[ iGroup ]
29: bottom_right[ iGroup ]
30: }
31: else if( slice_group_map_type = = 3 | |
32: slice_group_map_type = = 4 | |
33: slice_group_map_type = = 5 ) {
34: // slice_group_change_direction_flag 与下一个句法元素一起指明确切的片组分割方法。
35: slice_group_change_direction_flag
36: // slice_group_change_rate_minus1 用以指明变量 SliceGroupChangeRAte
37: slice_group_change_rate_minus1
38: } else if( slice_group_map_type = = 6 ) {
39: // pic_size_in_map_units_minus1 在片组类型等于6时,用以指明图像以 map_units 为单位的大小。
40: pic_size_in_map_units_minus1
41: for( i = 0; i <= pic_size_in_map_units_minus1; i++ )
42: // slice_group_id[i] 在片组类型等于6时,用以指明某个 map_units 属于哪个片组。
43: slice_group_id[ i ]
44: }
45: }
46: // num_ref_idx_l0_active_minus1 加1后指明目前参考帧队列的长度,即有多少个参考帧(包括短期和长期)。值得注意的是,当目前解码图像是场模式下,参考帧队列的长度应该是本句法元素再乘以2,因为场模式下各帧必须被分解以场对形式存在。(这里所说的场模式包括图像的场及帧场自适应下的处于场模式的宏块对) 本句法元素的值有可能在片头被重载。
47: 在序列参数集中有句法元素 num_ref_frames 也是跟参考帧队列有关,它们的区别是num_ref_frames指明参考帧队列的最大值,解码 器用它的值来分配内存空 间;num_ref_idx_l0_active_minus1 指明在这个队列中当前实际的、已存在的参考帧数目,这从它的名字“active”中也可以看出来。图像时,并不是直接传送该图像的编号,而是传送该图像在参考帧队列中的序号。这个序号并不是在码流中传送的,这个句法元素是 H.264 中最重要的句法元素之一,编码器要通知解码器某个运动矢量所指向的是哪个参考而是编码器和解码器同步地、用相同的方法将参考图像放入队列,从而获得一个序号。这个队列在每解一个图像,甚至是每个片后都会动态地更新。维护参考帧队列是编解码器十分重要的工作,而本句法元素是维护参考帧队列的重要依据。参考帧队列的复杂的维护机制是 H.264 重要也是很有特色的组成部分
48: num_ref_idx_l0_active_minus1
49: num_ref_idx_l1_active_minus1
50: // weighted_pred_flag 用以指明是否允许P和SP片的加权预测,如果允许,在片头会出现用以计算加权预测的句法元素。
51: weighted_pred_flag
52: // weighted_bipred_flag 用以指明是否允许 B 片的加权预测,本句法元素等于 0 时表示使用默认加权预测模式,等于 1 时表示使用显式加权预测模式,等于 2 时表示使用隐式加权预测模式。
53: weighted_bipred_idc
54: // pic_init_qp_minus26 加 26 后用以指明亮度分量的量化参数的初始值。在 H.264 中,量化参数分三个级别给出:图像参数集、片头、宏块。在图像参数集给出的是一个初始值。
55: pic_init_qp_minus26 /* relative to 26 */
56: pic_init_qs_minus26 /* relative to 26 */
57: // chroma_qp_index_offset 色度分量的量化参数是根据亮度分量的量化参数计算出来的,本句法元素用以指明计算时用到的参数。
58: chroma_qp_index_offset
59: // deblocking_filter_control_present_flag 编码器可以通过句法元素显式地控制去块滤波的强度,本句法元素指明是在片头是否会有句法元素传递这个控制信息。如果本句法元素等于 0,那些用于传递滤波强度的句法元素不会出现,解码器将独立地计算出滤波强度。
60: deblocking_filter_control_present_flag
61: // constrained_intra_pred_flag 在 P 和 B 片中,帧内编码的宏块的邻近宏块可能是采用的帧间编码。当本句法元素等于 1 时,表示帧内编码的宏块不能用帧间编码的宏块的像素作为自己的预测,即帧内编码的宏块只能用邻近帧内编码的宏块的像素作为自己的预测;而本句法元素等于 0 时,表示不存在这种限制。
62: constrained_intra_pred_flag
63: // redundant_pic_cnt_present_flag 指明是否会出现 redundant_pic_cnt 句法元素。
64: redundant_pic_cnt_present_flag
65: rbsp_trailing_bits( )
66: }