H264语法分析

一 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

2.4片层句法

1片层句法(不分区)

句法

C

Desc

slice_layer_without_partitioning_rbsp(){

 

 

    slice_header()

2

 

    slice_data() /* all categories of slice_data() syntax */

2|3|4

 

    rbsp_slice_trailing_bits()

2

 

}

 

 

表6

2 片层A分区句法

句法

C

Desc

slice_data_partition_a_layer_rbsp(){

 

 

    slice_header()

2

 

    slice_id

2

ue(v)

    slice_data() /* only category 2 parts of slice_data() syntax */

2

 

    rbsp_slice_trailing_bits()

2

 

}

 

 

表7

3 片层B分区句法

句法

C

Desc

slice_data_partition_b_layer_rbsp(){

 

 

    slice_id

3

ue(v)

    if(redundant_pic_cnt_present_flag)

 

 

        redundant_pic_cnt

3

 

    slice_data() /* only category 3 parts of slice_data() syntax */

3

ue(v)

    rbsp_slice_trailing_bits()

3

 

}

 

 

表8

4 片层C分区句法

句法

C

Desc

slice_data_partition_c_layer_rbsp(){

 

 

    slice_id

4

ue(v)

    if(redundant_pic_cnt_present_flag)

 

 

        redundant_pic_cnt

4

 

    slice_data() /* only category 4 parts of slice_data() syntax */

4

ue(v)

    rbsp_slice_trailing_bits()

4

 

}

 

 

表9

5 拖尾(trailing bits)句法

句法

C

Desc

rbsp_trailing_bits(){

 

 

    rbsp_stop_one_bit /* equal to 1 */

All

f(1)

    while(!byte_aligned())

 

 

        rbsp_alignment_zero_bit /* equal to 0 */

All

f(1)

}

 

 

表10

6 片头(Slice header)句法

句法

C

Desc

slice_header(){

 

 

    first_mb_in_slice /* 片中的第一个宏块地址,片通过这个句法元素来标定它自己的地址。在帧场自适应模式下,宏块都是成对出现,这时本句法元素表示的是第几个宏块对,对应的第一个宏块的真实地址应该是:2*first_mb_in_slice */

2

ue(v)

    slice_type /* 片的类型,见表12 */

2

ue(v)

    pic_parameter_set_id /* 引用的图像集索引 */

2

ue(v)

    frame_num /* 每个参考帧都有一个连续的frame_num作为它们的标识,它指明了各图像的解码顺序。非参考帧也有,但没有意义。 */

2

u(v)

    if(!frame_mbs_only_flag){

 

 

        field_pic_flag /* 片层中标识图像编码模式的唯一一个元素,详细描述,见图3 */

2

u(1)

        if(filed_pic_flag)

 

 

            bottom_field_flag /* 1:底场,0:顶场 */

2

u(1)

    }

 

 

    if(nal_unit_type==5)

 

 

        idr_pic_id /* IDR图像标识,不同的IDR图像有不同的IDR值。场模式下,IDR帧的两个场有相同的idr_pic_id值,[0..65535] */

2

ue(v)

    if(pic_order_cnt_type==0){

 

 

        pic_order_cnt_lsb/* 在poc的第一种算法中,显示传递poc的值,u(v)中,v=log2_max_pic_order_cnt_lsb_minus4+4 */

2

u(v)

        if(pic_order_present_flag && !field_pic_flag)

 

 

            delta_pic_order_cnt_bottom /* 如果是在场模式下,场对中的两个场都各自被构造为一个图像,它们有各自的poc的计算方法来分别计算两场的poc,也就是一个场对拥有一对poc值;而在帧模式或帧场自适应模式下,一个图像只能根据片头中的元素计算出一个poc。在frame_mbs_only_flag不为1时,每个帧或场自适应的图像在解码时,帧或帧场自适应中包含的两个场也必须有各自的poc值,通过本元素,可以在已经解码的帧或帧场自适应图像的poc基础上新映射一个poc,并把它赋给底场。*/

2

se(v)

    }

 

 

    if(pic_order_cnt_type==1 && !delta_pic_order_always_zero_flag){

 

 

        delta_pci_order_cnt[0] /* poc的第二和第三种算法是从frame_num中映射得来,本元素用于帧编码下的底场和场编码方式的场 */

2

se(v)

        if(pic_order_present_flag && !field_pic_flag)

 

 

            delta_pic_order_cnt[1] /* 用于帧编码下的顶场*/

2

se(v)

    }

 

 

    if(redundant_ic_cnt_present_flag)

 

 

        redundant_pic_cnt /* 冗余片的id号 */

2

ue(v)

    if(slice_type==B)

 

 

        direct_spatial_mv_pred_flag /* B图像在直接预测模式下,1:空间预测,0:时间预测 */

2

u(1)

    if(slice_type==P||slice_type==SP||slice_type==B){

 

 

        num_ref_idx_active_override_flag /* 重载PPS中的参考帧队列中实际可用的参考帧的数目。 */

2

u(1)

        if(num_ref_idx_active_override_flag){

 

 

            num_ref_idx_10_active_minus1 /* 重载值 */

2

ue(v)

            if(slice_type==B)

 

 

                num_ref_idx_11_active_minus1 /* 重载值 */

2

ue(v)

        }

 

 

    }

 

 

    ref_pic_list_reordering() /* 见第7节描述 */

2

 

    if((weighted_pred_flag && (slice_type==P || slice_type==SP)) || (weighted_bipred_idc==1 && slice_type==B))

 

 

        pred_weight_table() /* 见第8节描述 */

2

 

    if(nal_ref_idc!=0)

 

 

        dec_ref_pic_marking() /* 见第9节描述 */

2

 

    if(entropy_coding_mode_flag && slice_type!=1 && slice_type != SI)

 

 

        cabac_init_idc  /* 给出cabac初始化时表格的选择,[0..2] */

2

ue(v)

    slice_qp_delta /* 指出用于当前片的所有宏块的量化参数的初始值。SliceQPY=26+pic_init_qp_minus26+slice_qp_delta,[0..51] */

2

se(v)

    if(slice_type==SP || slice_type==SI){

 

 

        if(slice_type==SP)

 

 

            sp_for_switch_flag /* 指出SP帧中的p宏块的解码方式是否是switching模式 */

2

u(1)

        slice_qs_delta /* 与slice_qp_delta的语义相似,用于SP和SI,QSY=26+pic_init_qs_minus26+slice_qs_delta,[0..51] */

2

se(v)

    }

 

 

    if(deblocking_flter_control_present_flag){

 

 

        disable_deblocking_filter_idc /* H.264指定了一套算法可以在解码器端独立地计算图像中各边界的滤波强度进行滤波。除了解码器独立计算之外,编码器也可以传递句法元素来干涉滤波强度,该元素指定了在块的是否使用滤波,同时批明那个块的边界不用滤波 */

2

ue(v)

        if(disable_deblocking_filter_idc != 1){

 

 

            slice_alpha_c0_offset_div2 /* 增强alpha时的偏移值,FilterOffsetA=slice_alpha_c0_offset_div2 << 1 */

2

se(v)

            slice_beta_offset_div2 /* 增强beta的偏移值,FilterOffsetB=slice_beta_offset_div2<<1 */

2

se(v)

        }

 

 

    }

 

 

    if(num_slice_group_minus1 > 0 && slice_group_map_type>=3 && slice_group_map_type <= 5)

 

 

    slice_group_change_cycle /* 片组类型是3.4.5时,由该元素可以获取片组中映射单元的数目 */

2

u(v)

}

 

 

表11

slice_type

name

0

P slice

1

B slice

2

I slice

3

SP slice

4

SI slice

5

P slice

6

B slice

7

I slice

8

SP slice

9

SI slice

 

表12(注:IDR图像时,slice_type为2,4,7,9)

 

图3

7 参考帧重排序

句法

C

Desc

ref_pic_list_reordering(){ /* 每个参考图像都有frame_num,但编码器要指定当前图像的参考图像时,使用是ref_id,frame_num->PicNum->ref_id */

 

 

    if(slice_type!=I && slice_type!=SI){

 

 

        ref_pic_list_reordering_flag_l0 /* 指明List0是否进行重排序 */

2

u(1)

        if(ref_pic_list_reordering_flag_l0)

 

 

            do{

 

 

                reordering_ofpic_nums_idc /* 执行哪种重排序操作,见表14描述 */

2

ue(v)

                if(reordering_ofpic_nums_idc==0 || reordering_ofpic_nums_idc==1)

 

 

                    abs_diff_pic_num_minus1 /* 对短期参考帧重排序时指明重排序图像与当前的差,见表14 */

2

ue(v)

                elseif(reordering_ofpic_nums_idc==2)

 

 

                    long_term_pic_num /* 对长期参考帧得排序时指明重排序图像 */

2

ue(v)

            }while(reordering_of_pic_nums_idc!=3)

 

 

    }

 

 

    if(slice_type==B){

 

 

        ref_pic_list_reordering_flag_l1 /* 指明List1是否进行重排序 */

2

u(1)

        if(ref_pic_list_reordering_flag_l1)

 

 

            do{

 

 

                reordering_ofpic_nums_idc /* 执行哪种重排序操作,见表14描述 */

2

ue(v)

                if(reordering_ofpic_nums_idc==0 || reordering_ofpic_nums_idc==1)

 

 

                    abs_diff_pic_num_minus1 /* 对短期参考帧重排序时指明重排序图像与当前的差,见表14 */

2

ue(v)

                elseif(reordering_ofpic_nums_idc==2)

 

 

                    long_term_pic_num /* 对长期参考帧得排序时指明重排序图像 */

2

ue(v)

            }while(reordering_of_pic_nums_idc!=3)

 

 

    }

 

 

}

 

 

表13

reordering_of_pic_nums_idc

操作

0

短期参考帧重排序,abs_diff_pic_num_minus1会出现在码流中,从当前图像的PicNum减去(abs_diff_pic_num_minus1+1)后指明需要重排序的图像

1

短期参考帧重排序,abs_diff_pic_num_minus1会出现在码流中,从当前图像的PicNum加上(abs_diff_pic_num_minus1+1)后指明需要重排序的图像

2

长期参考帧重排序,long_term_pic_num会出现在码流中,指明需要重排序的图像。

3

结束循环,退出重排序操作。

表14

8 加权预测的语义

句法

C

Desc

pred_weight_table(){

 

 

    luma_log2_weight_denom /* 给出参考帧列表中参考图像所有亮度的加权系数,[0..7] */

2

ue(v)

    chroma_log2_weight_denom /* 给出参考帧列表中参考图像所有色度的加权系数,[0..7] */

2

ue(v)

    for(i=0;i<=num_ref_idx_l0_active_minus1;i++){

 

 

        luma_weight_10_flag /* 1:在参考帧序列0中的亮度的加权系数存在 */

2

u(1)

        if(luma_weight_l0_flag){

 

 

            luma_weight_l0[i] /* 用参考序列0预测亮度值时,所用的加权系数。如果luma_weight_l0_flag=0,luma_weight_l0[i]=2luma_log2_weight_denom */

2

se(v)

            luma_offset_l0[i] /* 用参考序列0预测亮度值时,所用的加权系数的偏移,[-128-..127],如果luma_weight_l0_flag=0,该值0 */

2

se(v)

        }

 

 

        chroma_weight_l0_flag /* 同Luma相似,但用于色度 */

2

u(1)

        if(chroma_weight_l0_flag){

 

 

            for(j=0;j<2;j++)

 

 

                chroma_weight_l0[i][j]

2

se(v)

                chroma_offset_l0[i][j]

2

se(v)

            }

 

 

    }

 

 

    if(slice_type==B)

 

 

        for(i=0;i<=num_ref_idx_l1_active_minus1;i++){

 

 

            luma_weight_l1_flag

2

u(1)

            if(luma_weight_l1_flag){

 

 

                luma_weight_l1[i]

2

se(v)

                luma_offset_l1[i]

2

se(v)

            }

 

 

            chroma_weight_l1_flag

2

u(1)

            if(chroma_weight_l1_flag)

 

 

                for(j=0;j<2;j++){

 

 

                    chroma_weight_l1[i][j]

2

se(v)

                    chroma_offset_l1[i][j]

2

se(v)

                }

 

 

        }

 

 

}

 

 

表15

9 参考图像序列标记

句法

C

Desc

dec_ref_pic_marking(){ /* 前文介绍的重排序操作是对参考帧队列重新排序,而标记操作负责将参考图像移入或移出参考帧队列 */

 

 

    if( nal_unit_type ==5){

 

 

        no_output_of_prior_pics_flag  /* IDR时,1:将前面已经解码的图像全部输出 */

2|5

u(1)

        long_term_reference_flag  /* IDR时,1:使用长期参考,并且每个IDR图像解码后自动成为长期参考帧,0:IDR图像解码后自动成为短期参考帧 */

2|5

u(1)

    }else{

 

 

        adaptive_ref_pic_marking_mode_flag /* 0:FIFO,使用滑动窗的机制,先入先出,在这种模式下,无法对长期参考帧进行操作,1:自适应标记,后续码流中会有一系列句法元素显式指明操作的步骤。 */

 

u(1)

        if(adaptive_ref_pic_marking_mode_flag){

 

 

            do{

 

 

                memory_management_control_operation /*自适应模式下,指明本次操作的具体内容,详细描述见表17 */

2|5

ue(v)

                if(memory_management_control_operation == 1 ||memory_management_control_operation==3)

 

 

                    difference_of_pic_nums_minus1/* 通过该元素可以计算出需要操作的图像在短期参考队列中的序号。 */

2|5

ue(v)

                if(memory_management_control_operation == 2)

 

 

                    long_term_pic_num /* 得到所要操作的长期参考图像的序号 */

2|5

ue(v)

                if(memory_management_control_operation == 3 ||memory_management_control_operation == 6)

 

 

                    long_term_frame_idx /* 分配一个长期参考帧的序号给一个图像 */

2|5

ue(v)

                if(memory_management_control_operation == 4)

 

 

                    max_long_tewrm_frame_idx_plus1/* 此元素减1,指明长期参考队列的最大数目,[0..num_ref_frames] */

2|5

ue(v)

            }while(memory_management_control_operation != 0)

 

 

    }

 

 

}

 

 

表16

 

memory_management_control_operation

操作

0

结束循环

1

将一个短期参考图像标记为非参考图像,也即将一个短期参考图像移出参考帧队列

2

将一个长期参考图像标记为非参考图像,也即将一个长期参考图像移出参考帧队列

3

将一个短期参考图像转为长期参考图像

4

指明长期参考帧的最大数目

5

清空参考帧队列,将所有参考图像移参考帧队列,并禁用长期参考机制

6

将当前图像存存为一个长期参考帧

表17标记操作

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值