h.264語法結構分析


NAL Unit Stream

Network Abstraction Layer,簡稱NAL。

h.264把原始的yuv文件編碼成碼流文件,生成的碼流文件就是NAL單元流(NAL unit Stream)。而NAL單元流,就是NAL單元組成的。

標准的Annex B規定了NAL單元組成NAL單元流的方式,下面描述了如何將一個NAL單元打包起來,而多個NAL單元進行組合則形成了NAL單元流。

byte_stream_nal_unit( NumBytesInNALunit ) { C Descriptor
  while( next_bits( 24 ) != 0x000001 &&
    next_bits( 32 ) != 0x00000001 )
    leading_zero_8bits /* equal to 0x00 */ f(8)
  if( next_bits( 24 ) != 0x000001 )
    zero_byte /* equal to 0x00 */ f(8)
  start_code_prefix_one_3bytes /* equal to 0x000001 */ f(24)
  nal_unit( NumBytesInNALunit )
  while( more_data_in_byte_stream( ) &&
    next_bits( 24 ) != 0x000001 &&
    next_bits( 32 ) != 0x00000001 )
    trailing_zero_8bits /* equal to 0x00 */ f(8)
}

   語法元素

  • leading_zero_8bits  0x00,只有可能出現在NAL單元流的頭部,但是一般編碼出來的h264文件都不會包含這部分。
  • zero_byte  0x00,如果當前的NAL單元為sps、pps或者一個訪問單元(access unit)的第一個NAL單元,這個字節就會存在。訪問單元代表一張編碼圖像,不包含sps、pps等外部數據,但是一幅編碼圖像有可能分成幾個slice,甚至再細分成data partition,因此訪問單元的第一個NAL單元就會是該圖像的第一個slice或者slice data partition A。
  • start_code_prefix_one_3bytes  0x000001,固定存在的NAL單元起始碼,用來指示下面為一個NAL單元。
  • nal_unit( NumBytesInNALunit )   NAL單元
  • trailing_zero_8bits  0x00,可能出現的NAL單元后的補零,但是一般編碼出來的h264文件都沒有包含這部分。

image

 

 

NAL Unit

NAL單元是對RBSP進行打包生成的,NAL單元有如下語法

nal_unit( NumBytesInNALunit ) { C Descriptor
  forbidden_zero_bit All f(1)
  nal_ref_idc All u(2)
  nal_unit_type All u(5)
  NumBytesInRBSP = 0
  nalUnitHeaderBytes = 1
  if( nal_unit_type = = 14 | | nal_unit_type = = 20 | |
    nal_unit_type = = 21 ) {
    if( nal_unit_type ! = 21 )
      svc_extension_flag All u(1)
    else
      avc_3d_extension_flag All u(1)
    if( svc_extension_flag ) {
      nal_unit_header_svc_extension( ) /* specified in Annex G */ All
      nalUnitHeaderBytes += 3
    } else if( avc_3d_extension_flag ) {
      nal_unit_header_3davc_extension( ) /* specified in Annex J */
      nalUnitHeaderBytes += 2
    } else {
      nal_unit_header_mvc_extension( ) /* specified in Annex H */ All
      nalUnitHeaderBytes += 3
    }
  }
  for( i = nalUnitHeaderBytes; i < NumBytesInNALunit; i++ ) {
    if( i + 2 < NumBytesInNALunit && next_bits( 24 ) = = 0x000003 ) {
      rbsp_byte[ NumBytesInRBSP++ ] All b(8)
      rbsp_byte[ NumBytesInRBSP++ ] All b(8)
      i += 2
      emulation_prevention_three_byte /* equal to 0x03 */ All f(8)
    } else
      rbsp_byte[ NumBytesInRBSP++ ] All b(8)
  }
}

 

   語法元素

  • forbidden_zero_bit  0,一個bit
  • nal_ref_idc  2個bit,用來指示當前NAL單元的優先級。0的優先級最低,如果當前NAL單元內是非參考圖像的slice或者slice data partition等不是那么重要的數據,那么這個值為0;如果當前NAL單元內是sps、pps、參考圖像的slice或者slice data partition等重要數據,那么這個值不為0
  • nal_unit_type  5個bit,用來指示當前NAL單元中包含的RBSP的結構,這些不同結構的RBSP由不同的RBSP語法生成,下一節我們將討論這些RBSP語法。
  • svc_extension_flag  1個bit,Scalable Video Coding,主要分為Temporal,Spatial,Quality三種不同的scalable coding,分別對應同一碼流內可以包含具有不同幀率、不同分辨率、不同碼率的分層編碼方式。特定的NAL type中才會出現這個位,這里不展開討論
  • avc_3d_extension_flag  1個bit,表示3D編碼,特定的NAL type中才會出現這個位,這里不作討論
  • rbsp_byte  表示被打包的RBSP字節
  • emulation_prevention_three_byte  0x03,用於防止競爭。前面討論過,NAL單元的頭部會出現0x000001或者0x00000001的情況,那如果RBSP中也出現這幾個字節就會把他們錯當成NAL單元的頭部了,為了防止這種情況,當RBSP中出現連續兩個字節為零(即0x0000)時,會在后面插入一個0x03。則有

0x000000 =>  0x00000300

0x000001 =>  0x00000301

0x000002 =>  0x00000302

0x000003 =>  0x00000303

……

image

 

 

RBSP

Raw Byte Sequence Payload,原始字節序列載荷。

跟據nal_unit_type可以分成以下表格

nal_unit_type Content of NAL unit and RBSP
syntax structure
Categary
0 Unspecified  
1 Coded slice of a non-IDR picture
slice_layer_without_partitioning_rbsp( )
2,3,4
2 Coded slice data partition A
slice_data_partition_a_layer_rbsp( )
2
3 Coded slice data partition B
slice_data_partition_b_layer_rbsp( )
3
4 Coded slice data partition C
slice_data_partition_c_layer_rbsp( )
4
5 Coded slice of an IDR picture
slice_layer_without_partitioning_rbsp( )
2,3
6 Supplemental enhancement information
(SEI)
sei_rbsp( )
5
7 Sequence parameter set
seq_parameter_set_rbsp( )
0
8 Picture parameter set
pic_parameter_set_rbsp( )
1
9 Access unit delimiter
access_unit_delimiter_rbsp( )
6
10 End of sequence
end_of_seq_rbsp( )
7
11 End of stream
end_of_stream_rbsp( )
8
12 Filler data
filler_data_rbsp( )
9
13 Sequence parameter set extension
seq_parameter_set_extension_rbsp( )
10
14 Prefix NAL unit
prefix_nal_unit_rbsp( )
2
15 Subset sequence parameter set
subset_seq_parameter_set_rbsp( )
0
16…18 Reserved  
19 Coded slice of an auxiliary coded
picture without partitioning
slice_layer_without_partitioning_rbsp( )
2,3,4
20 Coded slice extension
slice_layer_extension_rbsp( )
2,3,4
21 Coded slice extension for a depth view
component or a 3D-AVC texture view component
slice_layer_extension_rbsp( )
2,3,4
22…23 Reserved  
24…31 Unspecified  

 

第一列代表當前NAL的類型;第二列是該類型對應的描述以及RBSP語法結構名稱;第三列列出了當前NAL類型中可能出現的語法元素種類,(Category)種類在所有語法結構中的語法元素后面都有標明。

 

 

RBSP尾部

標准中描述了很多種的RBSP結構並且通過語法表現出來,RBSP語法主要規定了該結構由什么成員組成,各個成員如何組合,成員會占用幾個bit。不過雖然RBSP結構有很多種,但是他們也有一個共同點:都有一個RBSP尾部。

RBSP尾部的語法如下:

rbsp_trailing_bits( ) { C Descriptor
  rbsp_stop_one_bit /* equal to 1 */ All f(1)
  while( !byte_aligned( ) )
    rbsp_alignment_zero_bit /* equal to 0 */ All f(1)
}

   語法元素

  • rbsp_stop_one_bit  1位的1
  • rbsp_alignment_zero_bit  字節補零,目的是為了進行字節對齊

 

有一種特殊情況:如果采用的熵編碼方式為CABAC,而且當前是實際圖像內容相關的RBSP(名稱包含slice的RBSP結構),那么會在RBSP尾部的后面添加1個或多個0x0000。語法表示如下:

rbsp_slice_trailing_bits( ) { C Descriptor
  rbsp_trailing_bits( ) All
  if( entropy_coding_mode_flag )
    while( more_rbsp_trailing_data( ) )
      cabac_zero_word /* equal to 0x0000 */ All f(16)
}

   語法元素

  • cabac_zero_word  0x0000

image

 

RBSP中除了rbsp_trailing_bits以及rbsp_slice_trailing_bits,其余部分被統稱為SODB(String Of Data Bits)。

 

 

seq_parameter_set_rbsp

這是SPS RBSP的名稱,它的結構如下

seq_parameter_set_rbsp( ) { C Descriptor
  seq_parameter_set_data( ) 0
  rbsp_trailing_bits( ) 0
}

如前面所說,RBSP都有一個rbsp_trailing_bits的尾部。而SPS的結構被包含在了seq_parameter_set_data里面

seq_parameter_set_data( ) { C Descriptor
  profile_idc 0 u(8)
  constraint_set0_flag 0 u(1)
  constraint_set1_flag 0 u(1)
  constraint_set2_flag 0 u(1)
  constraint_set3_flag 0 u(1)
  constraint_set4_flag 0 u(1)
  constraint_set5_flag 0 u(1)
  reserved_zero_2bits /* equal to 0 */ 0 u(2)
  level_idc 0 u(8)
  seq_parameter_set_id 0 ue(v)
  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 ) {
    chroma_format_idc 0 ue(v)
    if( chroma_format_idc = = 3 )
      separate_colour_plane_flag 0 u(1)
    bit_depth_luma_minus8 0 ue(v)
    bit_depth_chroma_minus8 0 ue(v)
    qpprime_y_zero_transform_bypass_flag 0 u(1)
    seq_scaling_matrix_present_flag 0 u(1)
    if( seq_scaling_matrix_present_flag )
      for( i = 0; i < ( ( chroma_format_idc != 3 ) ? 8 : 12 ); i++ ) {
        seq_scaling_list_present_flag[ i ] 0 u(1)
        if( seq_scaling_list_present_flag[ i ] )
        if( i < 6 )
          scaling_list( ScalingList4x4[ i ], 16,
          UseDefaultScalingMatrix4x4Flag[ i ]) 0
        else
          scaling_list( ScalingList8x8[ i − 6 ], 64,
          UseDefaultScalingMatrix8x8Flag[ i − 6 ] ) 0
      }
  }
  log2_max_frame_num_minus4 0 ue(v)
  pic_order_cnt_type 0 ue(v)
  if( pic_order_cnt_type = = 0 )
    log2_max_pic_order_cnt_lsb_minus4 0 ue(v)
  else if( pic_order_cnt_type = = 1 ) {
    delta_pic_order_always_zero_flag 0 u(1)
    offset_for_non_ref_pic 0 se(v)
    offset_for_top_to_bottom_field 0 se(v)
    num_ref_frames_in_pic_order_cnt_cycle 0 ue(v)
    for( i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++ )
      offset_for_ref_frame[ i ] 0 se(v)
  }
  max_num_ref_frames
  gaps_in_frame_num_value_allowed_flag 0 u(1)
  pic_width_in_mbs_minus1 0 ue(v)
  pic_height_in_map_units_minus1 0 ue(v)
  frame_mbs_only_flag 0 u(1)
  if( !frame_mbs_only_flag )
    mb_adaptive_frame_field_flag 0 u(1)
  direct_8x8_inference_flag 0 u(1)
  frame_cropping_flag 0 u(1)
  if( frame_cropping_flag ) {
    frame_crop_left_offset 0 ue(v)
    frame_crop_right_offset 0 ue(v)
    frame_crop_top_offset 0 ue(v)
    frame_crop_bottom_offset 0 ue(v)
  }
  vui_parameters_present_flag 0 u(1)
  if( vui_parameters_present_flag )
    vui_parameters( ) 0
}

 

   語法元素

  • profile_idc  本視頻編碼時遵循的profile,profile分為Baseline,Main,Extended等,主要用來規定編碼時是否采用某些特性,比如說Baseline profile就規定了只能使用I、P slice進行編碼,關於profile的說明可以去查看標准的Annex A。
  • constraint_set0_flag 強制使用Baseline profile進行編碼
  • constraint_set1_flag 強制使用Main profile進行編碼
  • constraint_set2_flag 強制使用Extended profile進行編碼
  • reserved_zero_2bits  兩個0bit的保留位
  • level_idc  本視頻遵循的level,level主要規定了每秒最多能處理多少個宏塊,最大的幀大小,最大的解碼緩存,最大比特率等這些性能相關的東西,如果是硬解碼,則比較容易出現由於視頻level太高而不能解碼的情況。
  • seq_parameter_set_id  本SPS的ID,這個ID主要是給PPS用的。
  • log2_max_frame_num_minus4
  • pic_order_cnt_type
  • log2_max_pic_order_cnt_lsb_minus4
  • delta_pic_order_always_zero_flag
  • offset_for_non_ref_pic
  • offset_for_top_to_bottom_field
  • num_ref_frames_in_pic_order_cnt_cycle
  • offset_for_ref_frame  以上這幾個參數的目的都是進行POC計算,請參考h.264的POC計算
  • max_num_ref_frames  參考幀最多能有多少個
  • gaps_in_frame_num_value_allowed_flag  由於碼流在傳輸過程中可能出現丟包的情況,從而導致中間有幀缺失,如果制定了這個標記,則會在解碼時對幀丟失的情況進行調整,否則就當作意外丟失處理。
  • pic_width_in_mbs_minus1 圖片寬度(宏塊為單位)-1
  • pic_height_in_map_units_minus1  圖片高度(宏塊為單位)-1
  • frame_mbs_only_flag  是否只進行幀編碼
  • mb_adaptive_frame_field_flag  是否進行幀場自適應編碼
  • direct_8x8_inference_flag  在進行B Direct預測時,是否子宏塊(8x8塊)中的4個4x4塊共用一個4x4的co-located,請參考h.264直接預測 中的direct_8x8_inference_flag關鍵字
  • frame_cropping_flag  是否需要對解碼后的圖片進行修剪
  • frame_crop_left_offset
  • frame_crop_right_offset
  • frame_crop_top_offset
  • frame_crop_bottom_offset  分別指示修剪的左右上下
  • vui_parameters_present_flag  SPS是否包含vui參數
  • vui_parameters  video usability information,在標准的Annex E中有描述,主要包含了視頻的比例調整,overscan,視頻格式,timing,比特率等信息。

 

 

pic_parameter_set_rbsp

如前面所說,RBSP都有一個rbsp_trailing_bits的尾部。PPS RBSP結構如下

pic_parameter_set_rbsp( ) { C Descriptor
  pic_parameter_set_id 1 ue(v)
  seq_parameter_set_id 1 ue(v)
  entropy_coding_mode_flag 1 u(1)
  bottom_field_pic_order_in_frame_present_flag 1 u(1)
  num_slice_groups_minus1 1 ue(v)
  if( num_slice_groups_minus1 > 0 ) {
    slice_group_map_type 1 ue(v)
    if( slice_group_map_type = = 0 )
      for( iGroup = 0; iGroup <= num_slice_groups_minus1; iGroup++ )
        run_length_minus1[ iGroup ] 1 ue(v)
    else if( slice_group_map_type = = 2 )
      for( iGroup = 0; iGroup < num_slice_groups_minus1; iGroup++ ) {
        top_left[ iGroup ] 1 ue(v)
        bottom_right[ iGroup ] 1 ue(v)
      }
    else if( slice_group_map_type = = 3 | |
      slice_group_map_type = = 4 | |
      slice_group_map_type = = 5 ) {
      slice_group_change_direction_flag 1 u(1)
      slice_group_change_rate_minus1 1 ue(v)
    } else if( slice_group_map_type = = 6 ) {
      pic_size_in_map_units_minus1 1 ue(v)
      for( i = 0; i <= pic_size_in_map_units_minus1; i++ )
        slice_group_id[ i ] 1 u(v)
    }
  }
  num_ref_idx_l0_default_active_minus1 1 ue(v)
  num_ref_idx_l1_default_active_minus1 1 ue(v)
  weighted_pred_flag 1 u(1)
  weighted_bipred_idc 1 u(2)
  pic_init_qp_minus26 /* relative to 26 */ 1 se(v)
  pic_init_qs_minus26 /* relative to 26 */ 1 se(v)
  chroma_qp_index_offset 1 se(v)
  deblocking_filter_control_present_flag 1 u(1)
  constrained_intra_pred_flag 1 u(1)
  redundant_pic_cnt_present_flag 1 u(1)
  if( more_rbsp_data( ) ) {
    transform_8x8_mode_flag 1 u(1)
    pic_scaling_matrix_present_flag 1 u(1)
    if( pic_scaling_matrix_present_flag )
      for( i = 0; i < 6 +
        ( ( chroma_format_idc != 3 ) ? 2 : 6 ) * transform_8x8_mode_flag;
        i++ ) {
        pic_scaling_list_present_flag[ i ] 1 u(1)
        if( pic_scaling_list_present_flag[ i ] )
          if( i < 6 )
            scaling_list( ScalingList4x4[ i ], 16,
            UseDefaultScalingMatrix4x4Flag[ i ] ) 1
          else
            scaling_list( ScalingList8x8[ i − 6 ], 64,
            UseDefaultScalingMatrix8x8Flag[ i − 6 ] ) 1
      }
    second_chroma_qp_index_offset 1 se(v)
  }
  rbsp_trailing_bits( ) 1
}

 

   語法元素

  • pic_parameter_set_id  當前PPS的ID,供slice RBSP使用
  • seq_parameter_set_id  當前PPS所屬的SPS的ID
  • entropy_coding_mode_flag  為0時表明采用CAVLC編碼,為1時采用CABAC編碼
  • bottom_field_pic_order_in_frame_present_flag  用於POC計算,請參考h.264的POC計算中的bottom_field_flag
  • num_slice_groups_minus1  進行圖像編碼時,分成幾個slice group,關於slice group請參考h.264 FMO
  • slice_group_map_type  如果num_slice_groups_minus1>0表明會分成多個slice group,此時會采用slice_group_map_type來規定宏塊的組合方式,這部分請參考h.264 FMO
  • num_ref_idx_l0_default_active_minus1
  • num_ref_idx_l1_default_active_minus1  分別指示前向參考圖像以及后向參考圖像的個數,請參考h.264參考圖像列表、解碼圖像緩存->參考圖像列表->長度
  • weighted_pred_flag  用於指示P,SP slice是否進行亮度的加權預測,請參考h.264加權預測
  • weighted_bipred_idc  用於指示B slice的加權預測,0:否  1:顯式加權預測  2:隱式加權預測,請參考h.264加權預測
  • pic_init_qp_minus26  用於計算初始QP(整個視頻QP的基准值),關於QP,請參考H.264 Quantization
  • pic_init_qs_minus26  用於計算SP,SI幀的初始QP
  • chroma_qp_index_offset  色度宏塊Cb與亮度宏塊Y的QP差值
  • deblocking_filter_control_present_flag  是否進行deblocking
  • constrained_intra_pred_flag  0:當宏塊進行intra編碼時,盡管周邊宏塊為inter編碼,也能用於當前宏塊的intra預測。1:當宏塊進行intra編碼時,只有為intra編碼的周邊宏塊才能用於當前宏塊的intra預測,請參考Intra Luma Prediction
  • redundant_pic_cnt_present_flag  如果當前視頻中冗余圖像的話(SI,SP),那么這個flag應該為1
  • transform_8x8_mode_flag  用於指示luma宏塊是否以8x8的方式進行DCT變換
    • 1:當前luma宏塊進行8x8 DCT
    • 0:當前luma宏塊進行4x4 DCT
    這個標記不會作用到chroma宏塊,除非序列格式為4:4:4。也就是說如果序列格式為4:2:0或者4:2:2,那么無論transform_8x8_mode_flag是什么,chroma宏塊都是采用4x4 DCT。而如果序列格式為4:4:4,chroma宏塊采用與luma宏塊相同的DCT。如果碼流中沒有出現這個標記,那么這個標記的值默認為0。
  • pic_scaling_matrix_present_flag  scaling list相關
  • second_chroma_qp_index_offset  色度宏塊Cr與亮度宏塊Y的QP差值

 

 

 

slice_layer_without_partitioning_rbsp

如果當前slice不采用slice data partition的RBSP結構的話,就會是這個RBSP結構,編碼時一般都會采用的這個RBSP結構。

我們知道編碼是以slice為單位的,這個結構內包含的就是視頻中編碼的主要內容,視頻圖像進行編碼后就會包含在這個結構內,也就是說編碼后的碼流中,大多數都是以這個結構的RBSP打包成的NAL unit。

語法結構如下

slice_layer_without_partitioning_rbsp( ) { C Descriptor
  slice_header( ) 2
  slice_data( ) /* all categories of slice_data( ) syntax */ 2 | 3 | 4
  rbsp_slice_trailing_bits( ) 2
}

其中分成slice_header,slice_data兩部分,最后是RBSP尾部。

 

slice_header

slice_header就是slice的頭部,其中包含的是本slice的相關參數,語法結構如下

slice_header( ) { C Descriptor
  first_mb_in_slice 2 ue(v)
  slice_type 2 ue(v)
  pic_parameter_set_id 2 ue(v)
  if( separate_colour_plane_flag = = 1 )
    colour_plane_id 2 u(2)
  frame_num 2 u(v)
  if( !frame_mbs_only_flag ) {
    field_pic_flag 2 u(1)
    if( field_pic_flag )
      bottom_field_flag 2 u(1)
  }
  if( IdrPicFlag )
    idr_pic_id 2 ue(v)
  if( pic_order_cnt_type = = 0 ) {
    pic_order_cnt_lsb 2 u(v)
    if( bottom_field_pic_order_in_frame_present_flag && !field_pic_flag )
      delta_pic_order_cnt_bottom 2 se(v)
  }
  if( pic_order_cnt_type = = 1 && !delta_pic_order_always_zero_flag ) {
    delta_pic_order_cnt[ 0 ] 2 se(v)
    if( bottom_field_pic_order_in_frame_present_flag && !field_pic_flag )
      delta_pic_order_cnt[ 1 ] 2 se(v)
  }
  if( redundant_pic_cnt_present_flag )
    redundant_pic_cnt 2 ue(v)
  if( slice_type = = B )
    direct_spatial_mv_pred_flag 2 u(1)
  if( slice_type = = P | | slice_type = = SP | | slice_type = = B ) {
    num_ref_idx_active_override_flag 2 u(1)
    if( num_ref_idx_active_override_flag ) {
      num_ref_idx_l0_active_minus1 2 ue(v)
      if( slice_type = = B )
        num_ref_idx_l1_active_minus1 2 ue(v)
    }
  }
  if( nal_unit_type = = 20 | | nal_unit_type = = 21 )
    ref_pic_list_mvc_modification( ) /* specified in Annex H */ 2
  else
    ref_pic_list_modification( ) 2
  if( ( weighted_pred_flag && ( slice_type = = P | | slice_type = = SP ) ) | |
    ( weighted_bipred_idc = = 1 && slice_type = = B ) )
    pred_weight_table( ) 2
  if( nal_ref_idc != 0 )
    dec_ref_pic_marking( ) 2
  if( entropy_coding_mode_flag && slice_type != I && slice_type != SI )
    cabac_init_idc 2 ue(v)
  slice_qp_delta 2 se(v)
  if( slice_type = = SP | | slice_type = = SI ) {
    if( slice_type = = SP )
      sp_for_switch_flag 2 u(1)
    slice_qs_delta 2 se(v)
  }
  if( deblocking_filter_control_present_flag ) {
    disable_deblocking_filter_idc 2 ue(v)
    if( disable_deblocking_filter_idc != 1 ) {
      slice_alpha_c0_offset_div2 2 se(v)
      slice_beta_offset_div2 2 se(v)
    }
  }
  if( num_slice_groups_minus1 > 0 &&
    slice_group_map_type >= 3 && slice_group_map_type <= 5)
    slice_group_change_cycle 2 u(v)
}

 

   語法元素

  • first_mb_in_slice  當前slice的第一個宏塊在圖像中的位置
  • slice_type  指示當前slice的類型,如下表。當slice_type為5~9的時候,就表明要求當前圖像的其他slice為slice_type%5,也就是要求當前圖像slice_type一致
slice_type Nane of slice_type
0 P(P slice)
1 B(B slice)
2 I(I slice)
3 SP(SP slice)
4 SI(SI slice)
5 P(P slice)
6 B(B slice)
7 I(I slice)
8 SP(SP slice)
9 SI(SI slice)
  • pic_parameter_set_id  當前slice所屬的PPS的ID
  • frame_num  用於POC計算,請參考h.264的POC計算中的frame_num
  • field_pic_flag  當前slice是否進行的是場編碼
  • bottom_field_flag  當前slice是否在底場(bottom field)
  • idr_pic_id  指示IDR圖片的ID
  • pic_order_cnt_lsb 
  • delta_pic_order_cnt_bottom
  • delta_pic_order_cnt  以上三個都是用於計算POC,請參考h.264的POC計算
  • redundant_pic_cnt  冗余圖像編碼相關
  • direct_spatial_mv_pred_flag  1:B幀direct編碼采用空域預測方式,0:B幀direct編碼采用時域預測方式,請參考h.264直接預測
  • num_ref_idx_active_override_flag  當前slice的參考圖像列表是否采用以下兩個長度而不用PPS規定的長度
  • num_ref_idx_l0_active_minus1  前向參考圖像列表長度
  • num_ref_idx_l1_active_minus1  后續參考圖像列表長度
  • ref_pic_list_modification  參考圖像列表重排序的參數結構語法,請結合標准並參考h.264參考圖像列表、解碼圖像緩存->參考圖像列表->參考圖像列表重排序
  • pred_weight_table  加權預測的參數結構語法,請結合標准並參考h.264加權預測
  • dec_ref_pic_marking  解碼圖像標記的參數結構語法,請結合標准並參考h.264參考圖像列表、解碼圖像緩存->解碼圖像緩存->解碼圖像標記過程
  • cabac_init_idc  cabac中m、n表的索引
  • slice_qp_delta  pic_init_qp_minus26 + 26 + slice_qp_delta將成為當前slice的初始QP
  • disable_deblocking_filter_idc
  • slice_alpha_c0_offset_div2
  • slice_beta_offset_div2  以上三個是deblocking的相關參數

 

slice_data

slice_data是slice的主體部分,當前slice內的宏塊編碼后的信息都在其中。

語法結構如下

slice_data( ) { C Descriptor
  if( entropy_coding_mode_flag )
    while( !byte_aligned( ) )
      cabac_alignment_one_bit 2 f(1)
  CurrMbAddr = first_mb_in_slice * ( 1 + MbaffFrameFlag )
  moreDataFlag = 1
  prevMbSkipped = 0
  do {
    if( slice_type != I && slice_type != SI )
      if( !entropy_coding_mode_flag ) {
        mb_skip_run 2 ue(v)
        prevMbSkipped = ( mb_skip_run > 0 )
        for( i=0; i<mb_skip_run; i++ )
        CurrMbAddr = NextMbAddress( CurrMbAddr )
        if( mb_skip_run > 0 )
        moreDataFlag = more_rbsp_data( )
      } else {
        mb_skip_flag 2 ae(v)
        moreDataFlag = !mb_skip_flag
      }
    if( moreDataFlag ) {
      if( MbaffFrameFlag && ( CurrMbAddr % 2 = = 0 | |
      ( CurrMbAddr % 2 = = 1 && prevMbSkipped ) ) )
        mb_field_decoding_flag 2 u(1) | ae(v)
      macroblock_layer( ) 2 | 3 | 4
    }
    if( !entropy_coding_mode_flag )
      moreDataFlag = more_rbsp_data( )
    else {
      if( slice_type != I && slice_type != SI )
        prevMbSkipped = mb_skip_flag
      if( MbaffFrameFlag && CurrMbAddr % 2 = = 0 )
        moreDataFlag = 1
      else {
        end_of_slice_flag 2 ae(v)
        moreDataFlag = !end_of_slice_flag
      }
    }
    CurrMbAddr = NextMbAddress( CurrMbAddr )
  } while( moreDataFlag )
}

 

可以注意到slice_data內頭部的對齊外,它由宏塊信息循環組合而成。

image

 

   語法元素

  • cabac_alignment_one_bit  如果是cabac,需要slice_data的頭部8bit對齊,因此可能需要插入1
  • mb_skip_run  如果當前采用的是CAVLC編碼方式,則會采用這個語法元素來表達skip宏塊(P_Skip,B_Skip),mb_skip_run代表的是當前這個宏塊以及它的后面共有多少個跳過宏塊,並且后面的skip宏塊都不會被編碼。
  • mb_skip_flag  如果當前采用的是CABAC編碼方式,則會采用這個語法元素來表達skip宏塊(P_Skip,B_Skip),跟mb_skip_run不同的是,每個skip宏塊都有一個mb_skip_flag
  • macroblock_layer  如果不是為skip宏塊的話,則表明本宏塊有編碼的數據,因此會進去macroblock_layer
  • end_of_slice_flag  如果采用的是CABAC編碼方式,還會用這個標記來表達當前宏塊是否為這個slice的最后一個宏塊

 

 

macroblock_layer

如前面所說,這里面的是宏塊編碼數據,語法結構如下

macroblock_layer( ) { C Descriptor
  mb_type 2 ue(v) | ae(v)
  if( mb_type = = I_PCM ) {
    while( !byte_aligned( ) )
      pcm_alignment_zero_bit 3 f(1)
    for( i = 0; i < 256; i++ )
      pcm_sample_luma[ i ] 3 u(v)
    for( i = 0; i < 2 * MbWidthC * MbHeightC; i++ )
      pcm_sample_chroma[ i ] 3 u(v)
  } else {
    noSubMbPartSizeLessThan8x8Flag = 1
    if( mb_type != I_NxN &&
      MbPartPredMode( mb_type, 0 ) != Intra_16x16 &&
      NumMbPart( mb_type ) = = 4 ) {
      sub_mb_pred( mb_type ) 2
      for( mbPartIdx = 0; mbPartIdx < 4; mbPartIdx++ )
        if( sub_mb_type[ mbPartIdx ] != B_Direct_8x8 ) {
          if( NumSubMbPart( sub_mb_type[ mbPartIdx ] ) > 1 )
            noSubMbPartSizeLessThan8x8Flag = 0
        } else if( !direct_8x8_inference_flag )
          noSubMbPartSizeLessThan8x8Flag = 0
    } else {
      if( transform_8x8_mode_flag && mb_type = = I_NxN )
        transform_size_8x8_flag 2 u(1) | ae(v)
      mb_pred( mb_type ) 2
    }
    if( MbPartPredMode( mb_type, 0 ) != Intra_16x16 ) {
      coded_block_pattern 2 me(v) | ae(v)
      if( CodedBlockPatternLuma > 0 &&
      transform_8x8_mode_flag && mb_type != I_NxN &&
      noSubMbPartSizeLessThan8x8Flag &&
      ( mb_type != B_Direct_16x16 | | direct_8x8_inference_flag ) )
      transform_size_8x8_flag 2 u(1) | ae(v)
    }
    if( CodedBlockPatternLuma > 0 | | CodedBlockPatternChroma > 0 | |
      MbPartPredMode( mb_type, 0 ) = = Intra_16x16 ) {
      mb_qp_delta 2 se(v) | ae(v)
      residual( 0, 15 ) 3 | 4
    }
  }
}

 

   語法元素

  • mb_type  當前宏塊的類型,對於不同的slice(I,P,B),同一個值會表示不同的類型。而且這個類型不僅僅表達宏塊的預測模式,分割方式,還有其他的一些信息,請參考h.264宏塊與子宏塊類型
  • pcm_alignment_zero_bit  在PCM情況下會要求進行字節對齊
  • pcm_sample_luma  亮度的PCM,一共有16x16個
  • pcm_sample_chroma  色度的PCM,根據yuv的格式不同會有不同的個數,一般的4:2:0格式有8x8x2個
  • sub_mb_pred  子宏塊預測的語法結構,子宏塊為8x8大小的宏塊,也就是說一個宏塊有4個子宏塊,在這個語法結構的內部會進行4次子宏塊預測
  • transform_size_8x8_flag  宏塊所采用的變換方法0:4x4DCT;1:8x8DCT。變換方法請參考H.264 Transform。按照上述語法描述,只有滿足了以下條件碼流中才會出現這個語法元素,如果該語法元素沒出現在碼流中則默認為0。
    • Intra:總開關transform_8x8_mode_flag為1,並且宏塊類型為I_NxN(參考h.264宏塊與子宏塊類型,如果是I_8x8則為1,如果是I_4x4則為0)。
    • Inter:總開關transform_8x8_mode_flag為1,並且宏塊的預測模式塊不小於8x8。另外如果宏塊采用了直接預測方式,並且預測模式為B_Direct_16x16,則需要direct_8x8_inference_flag為1。(此時碼流中transform_size_8x8_flag的值為0或者1,是在編碼端由算法自行決定的)。
    • Intra_16x16以及I_PCM的情況下碼流中不存在該語法元素。
  • mb_pred  宏塊預測的語法結構,宏塊預測與子宏塊預測的語法結構是相斥的,一個宏塊的組成結構要么采用的是宏塊預測的結構,要么4個子宏塊都是子宏塊的預測結構
  • coded_block_pattern  簡稱CBP,用來反映該宏塊編碼中殘差情況的語法元素。CBP共有6位,其中前面2位代表UV分量,描述如下表所示;后面4位是Y分量,分別代表宏塊內的4個8x8子宏塊,如果任意一位為0,表明對應的8x8塊中所有變換系數level(transform coefficient levels 也就是對像素殘差進行變換、量化后的矩陣內的值,以后統稱level)全部都是0,否則表明對應的8x8塊中的變換系數level不全為0。另外需要注意的是,如果當前宏塊的預測模式是Intra_16x16,則不會存在這個元素,此時CBP會由mb_type來表示,請參考h.264宏塊與子宏塊類型。CBP的主要作用是加快解碼速度,當一個塊的殘差都為0時,就不用對這個塊進行殘差解碼了。
    CodedBlockPatternChroma Description
    0 All chroma transform coefficient levels are equal to 0.
    1 One or more chroma DC transform coefficient levels shall be non-zero valued.
    All chroma AC transform coefficient levels are equal to 0.
    2 Zero or more chroma DC transform coefficient levels are non-zero valued.
    One or more chroma AC transform coefficient levels shall be non-zero valued.
  • mb_qp_delta  用來計算當前宏塊的QP,QP=pic_init_qp_minus26 + 26 + slice_qp_delta + mb_qp_delta
  • residual  像素殘差編碼的語法結構。

 

按照macroblock_layer的語法結構上看,宏塊能粗略分成三種結構:PCM、sub_mb_pred(子宏塊預測)、mb_pred(宏塊預測)。另外,雖然skip宏塊並不在macroblock內描述,它也是宏塊的一種結構。

image

 

 

mb_perd

語法結構如下

mb_pred( mb_type ) { C Descriptor
  if( MbPartPredMode( mb_type, 0 ) = = Intra_4x4 | |
    MbPartPredMode( mb_type, 0 ) = = Intra_8x8 | |
    MbPartPredMode( mb_type, 0 ) = = Intra_16x16 ) {
    if( MbPartPredMode( mb_type, 0 ) = = Intra_4x4 )
      for( luma4x4BlkIdx=0; luma4x4BlkIdx<16; luma4x4BlkIdx++ ) {
        prev_intra4x4_pred_mode_flag[ luma4x4BlkIdx ] 2 u(1) | ae(v)
        if( !prev_intra4x4_pred_mode_flag[ luma4x4BlkIdx ] )
        rem_intra4x4_pred_mode[ luma4x4BlkIdx ] 2 u(3) | ae(v)
      }
    if( MbPartPredMode( mb_type, 0 ) = = Intra_8x8 )
      for( luma8x8BlkIdx=0; luma8x8BlkIdx<4; luma8x8BlkIdx++ ) {
        prev_intra8x8_pred_mode_flag[ luma8x8BlkIdx ] 2 u(1) | ae(v)
        if( !prev_intra8x8_pred_mode_flag[ luma8x8BlkIdx ] )
          rem_intra8x8_pred_mode[ luma8x8BlkIdx ] 2 u(3) | ae(v)
      }
    if( ChromaArrayType = = 1 | | ChromaArrayType = = 2 )
      intra_chroma_pred_mode 2 ue(v) | ae(v)
  } else if( MbPartPredMode( mb_type, 0 ) != Direct ) {
    for( mbPartIdx = 0; mbPartIdx < NumMbPart( mb_type ); mbPartIdx++)
      if( ( num_ref_idx_l0_active_minus1 > 0 | |
        mb_field_decoding_flag != field_pic_flag ) &&
        MbPartPredMode( mb_type, mbPartIdx ) != Pred_L1 )
        ref_idx_l0[ mbPartIdx ] 2 te(v) | ae(v)
    for( mbPartIdx = 0; mbPartIdx < NumMbPart( mb_type ); mbPartIdx++)
      if( ( num_ref_idx_l1_active_minus1 > 0 | |
        mb_field_decoding_flag != field_pic_flag ) &&
        MbPartPredMode( mb_type, mbPartIdx ) != Pred_L0 )
        ref_idx_l1[ mbPartIdx ] 2 te(v) | ae(v)
    for( mbPartIdx = 0; mbPartIdx < NumMbPart( mb_type ); mbPartIdx++)
      if( MbPartPredMode ( mb_type, mbPartIdx ) != Pred_L1 )
        for( compIdx = 0; compIdx < 2; compIdx++ )
          mvd_l0[ mbPartIdx ][ 0 ][ compIdx ] 2 se(v) | ae(v)
    for( mbPartIdx = 0; mbPartIdx < NumMbPart( mb_type ); mbPartIdx++)
      if( MbPartPredMode( mb_type, mbPartIdx ) != Pred_L0 )
        for( compIdx = 0; compIdx < 2; compIdx++ )
          mvd_l1[ mbPartIdx ][ 0 ][ compIdx ] 2 se(v) | ae(v)
  }
}

 

   語法元素

  • prev_intra4x4_pred_mode_flag  如果當前宏塊采用的是intra4x4的預測方式,則會存在這個語法元素,它的含義請參考Intra Luma Prediction
  • rem_intra4x4_pred_mode  如果當前宏塊采用的是intra4x4的預測方式,則可能會存在這個語法元素,它的含義請參考Intra Luma Prediction
  • prev_intra8x8_pred_mode_flag  如果當前宏塊采用的是intra8x8的預測方式,則會存在這個語法元素,它的含義請參考Intra Luma Prediction
  • rem_intra8x8_pred_mode  如果當前宏塊采用的是intra8x8的預測方式,則可能會存在這個語法元素,它的含義請參考Intra Luma Prediction
  • intra_chroma_pred_mode  intra chroma的預測模式,只有當前宏塊的Luma部分采用intra預測時才會存在這個語法元素,它的含義請參考Intra Chroma Prediction
  • ref_idx_l0  前向參考圖像索引。如果當前宏塊為inter預測,並且他的預測方式並非后向預測(即可能為前向預測或雙向預測),則會存在這個語法元素
  • ref_idx_l1  后向參考圖像索引。如果當前宏塊為inter預測,並且他的預測方式並非前向預測(即可能為后向預測或雙向預測),則會存在這個語法元素
  • mvd_l0  前向向量殘差。如果當前宏塊為inter預測,並且他的預測方式並非后向預測(即可能為前向預測或雙向預測),則會存在這個語法元素
  • mvd_l1  后向向量殘差。如果當前宏塊為inter預測,並且他的預測方式並非前向預測(即可能為后向預測或雙向預測),則會存在這個語法元素

 

下圖分別是幾個mb_pred結構的例子

  • 在intra16x16的宏塊模式下,intra16x16的宏塊信息是被包含在mb_type里面的,因此mb_pred結構內只有chroma相關的信息
  • 在intra8x8的宏塊模式下,共有四個子宏塊,因此分成4個部分
  • 在intra4x4的宏塊模式下,共有16個4x4塊,因此分成16部分
  • 如果是Pslice的inter宏塊,並且宏塊采用16x8的分割方式,那么宏塊會被分割成兩部分,因此會有兩個refIdx以及兩個mvd
  • 如果是Bslice的inter宏塊,並且宏塊采用16x16的分割方式,那么宏塊不會被分割,如果這個沒被宏塊采用的是雙向預測方式,那么會有前、后向的refIdx以及mvd
  • 如果是Bslice的inter宏塊,並且宏塊采用8x16的分割方式,那么宏塊會被分割成兩部分,如果第一部分采用的是前向預測方式,第二部分采用的是雙向預測方式,那么mb_pred內會有兩個前向、一個后向refIdx以及mvd

image

 

 

sub_mb_pred

語法結構如下

sub_mb_pred( mb_type ) { C Descriptor
  for( mbPartIdx = 0; mbPartIdx < 4; mbPartIdx++ )
    sub_mb_type[ mbPartIdx ] 2 ue(v) | ae(v)
  for( mbPartIdx = 0; mbPartIdx < 4; mbPartIdx++ )
    if( ( num_ref_idx_l0_active_minus1 > 0 | |
      mb_field_decoding_flag != field_pic_flag ) &&
      mb_type != P_8x8ref0 &&
      sub_mb_type[ mbPartIdx ] != B_Direct_8x8 &&
      SubMbPredMode( sub_mb_type[ mbPartIdx ] ) != Pred_L1 )
      ref_idx_l0[ mbPartIdx ] 2 te(v) | ae(v)
  for( mbPartIdx = 0; mbPartIdx < 4; mbPartIdx++ )
    if( ( num_ref_idx_l1_active_minus1 > 0 | |
      mb_field_decoding_flag != field_pic_flag ) &&
      sub_mb_type[ mbPartIdx ] != B_Direct_8x8 &&
      SubMbPredMode( sub_mb_type[ mbPartIdx ] ) != Pred_L0 )
      ref_idx_l1[ mbPartIdx ] 2 te(v) | ae(v)
  for( mbPartIdx = 0; mbPartIdx < 4; mbPartIdx++ )
    if( sub_mb_type[ mbPartIdx ] != B_Direct_8x8 &&
      SubMbPredMode( sub_mb_type[ mbPartIdx ] ) != Pred_L1 )
      for( subMbPartIdx = 0;
        subMbPartIdx < NumSubMbPart( sub_mb_type[ mbPartIdx ] );
        subMbPartIdx++)
        for( compIdx = 0; compIdx < 2; compIdx++ )
          mvd_l0[ mbPartIdx ][ subMbPartIdx ][ compIdx ] 2 se(v) | ae(v)
  for( mbPartIdx = 0; mbPartIdx < 4; mbPartIdx++ )
    if( sub_mb_type[ mbPartIdx ] != B_Direct_8x8 &&
      SubMbPredMode( sub_mb_type[ mbPartIdx ] ) != Pred_L0 )
      for( subMbPartIdx = 0;
        subMbPartIdx < NumSubMbPart( sub_mb_type[ mbPartIdx ] );
        subMbPartIdx++)
        for( compIdx = 0; compIdx < 2; compIdx++ )
          mvd_l1[ mbPartIdx ][ subMbPartIdx ][ compIdx ] 2 se(v) | ae(v)
}

 

   語法元素

  • sub_mb_type  子宏塊模式,子宏塊大小為8x8,因此一個宏塊內有4個子宏塊,也就會有4種子宏塊模式,具體請參考h.264宏塊與子宏塊類型
  • ref_idx_l0
  • ref_idx_l1  描述同mb_pred,不過需要注意的一點是,由於在8x8的子宏塊中,分塊(2個8x4塊,4個4x4塊等)是共用參考圖像的,也就是說整個宏塊最多也就只包含四個ref_idx
  • mvd_l0
  • mvd_l1 描述同mb_pred

 

下面是一個sub_mb_pred語法結構的例子。假設處於Bslice,左邊的表格分別代表四個子宏塊的模式。在該sub_mb_pred結構中

  • 頭部保存的是4個子宏塊各自的子宏塊類型
  • 接下來是前向refIdx,第一個子宏塊的預測方式為Bi,因此有前向refIdx;第二個子宏塊的預測方式為L0,也有前向refIdx;第三個子宏塊為直接預測,沒有refIdx;第四個子宏塊為L0,有前向refIdx
  • 然后是后向refIdx,只有第一個子宏塊的Bi會包含后向refIdx
  • 然后是前向mvd,第一個子宏塊分割方式為4x8,分割成兩個部分,因此有兩個前向mvd;第二個子宏塊分割方式為8x8,即不分割,因此只有一個前向mvd;第三個子宏塊為直接預測,沒有mvd;第四個子宏塊分割方式為8x4,分割成兩個部分,因此有兩個前向mvd
  • 最后是后向mvd,例子中只有第一個子宏塊,也就是采用Bi預測的會有后向mvd,由於第一個子宏塊被分割成兩部分,因此有兩個后向mvd

圖例中,結構上面的數字代表了該語法元素所屬的子宏塊。

image

 

 

residual,residual_luma

像素殘差進行變換、量化后的系數的語法結構。

residual( startIdx, endIdx ) { C Descriptor
  if( !entropy_coding_mode_flag )
    residual_block = residual_block_cavlc
  else
    residual_block = residual_block_cabac
  residual_luma( i16x16DClevel, i16x16AClevel, level4x4, level8x8,
  startIdx, endIdx ) 3 | 4
  Intra16x16DCLevel = i16x16DClevel
  Intra16x16ACLevel = i16x16AClevel
  LumaLevel4x4 = level4x4
  LumaLevel8x8 = level8x8
  if( ChromaArrayType = = 1 | | ChromaArrayType = = 2 ) {
    NumC8x8 = 4 / ( SubWidthC * SubHeightC )
    for( iCbCr = 0; iCbCr < 2; iCbCr++ )
      if( ( CodedBlockPatternChroma & 3 ) && startIdx = = 0 )
        /* chroma DC residual present */
        residual_block( ChromaDCLevel[ iCbCr ], 0, 4 * NumC8x8 − 1,
        4 * NumC8x8 ) 3 | 4
      else
        for( i = 0; i < 4 * NumC8x8; i++ )
          ChromaDCLevel[ iCbCr ][ i ] = 0
    for( iCbCr = 0; iCbCr < 2; iCbCr++ )
      for( i8x8 = 0; i8x8 < NumC8x8; i8x8++ )
        for( i4x4 = 0; i4x4 < 4; i4x4++ )
          if( CodedBlockPatternChroma & 2 )
            /* chroma AC residual present */
            residual_block( ChromaACLevel[ iCbCr ][ i8x8*4+i4x4 ],
            Max( 0, startIdx − 1 ), endIdx − 1, 15) 3 | 4
          else
            for( i = 0; i < 15; i++ )
              ChromaACLevel[ iCbCr ][ i8x8*4+i4x4 ][ i ] = 0
  } else if( ChromaArrayType = = 3 ) {
    residual_luma( i16x16DClevel, i16x16AClevel, level4x4, level8x8,
    startIdx, endIdx ) 3 | 4
    CbIntra16x16DCLevel = i16x16DClevel
    CbIntra16x16ACLevel = i16x16AClevel
    CbLevel4x4 = level4x4
    CbLevel8x8 = level8x8
    residual_luma( i16x16DClevel, i16x16AClevel, level4x4, level8x8,
    startIdx, endIdx ) 3 | 4
    CrIntra16x16DCLevel = i16x16DClevel
    CrIntra16x16ACLevel = i16x16AClevel
    CrLevel4x4 = level4x4
    CrLevel8x8 = level8x8
  }
}

 

residual內部首先會根據entropy_coding_mode_flag來選擇CAVLC或者CABAC的熵編碼方式,然后在下面進行level的處理。level處理部分先包含了residual_luma,也就是先進行luma level的處理,然后用residual_block對chroma level進行處理。

chroma level一般采用的yuv格式都是4:2:0,也就是ChromaArrayType=1。

 

residual_luma( i16x16DClevel, i16x16AClevel, level4x4, level8x8,
  startIdx, endIdx ) { C Descriptor
  if( startIdx = = 0 && MbPartPredMode( mb_type, 0 ) = = Intra_16x16 )
    residual_block( i16x16DClevel, 0, 15, 16 ) 3
  for( i8x8 = 0; i8x8 < 4; i8x8++ )
    if( !transform_size_8x8_flag | | !entropy_coding_mode_flag )
      for( i4x4 = 0; i4x4 < 4; i4x4++ ) {
        if( CodedBlockPatternLuma & ( 1 << i8x8 ) )
          if( MbPartPredMode( mb_type, 0 ) = = Intra_16x16 )
            residual_block( i16x16AClevel[i8x8*4+ i4x4],
            Max( 0, startIdx − 1 ), endIdx − 1, 15) 3
          else
            residual_block( level4x4[ i8x8 * 4 + i4x4 ],
            startIdx, endIdx, 16) 3 | 4
        else if( MbPartPredMode( mb_type, 0 ) = = Intra_16x16 )
          for( i = 0; i < 15; i++ )
            i16x16AClevel[ i8x8 * 4 + i4x4 ][ i ] = 0
        else
          for( i = 0; i < 16; i++ )
            level4x4[ i8x8 * 4 + i4x4 ][ i ] = 0
        if( !entropy_coding_mode_flag && transform_size_8x8_flag )
          for( i = 0; i < 16; i++ )
            level8x8[ i8x8 ][ 4 * i + i4x4 ] = level4x4[ i8x8 * 4 + i4x4 ][ i ]
      }
    else if( CodedBlockPatternLuma & ( 1 << i8x8 ) )
      residual_block( level8x8[ i8x8 ], 4 * startIdx, 4 * endIdx + 3, 64 ) 3 | 4
    else
      for( i = 0; i < 64; i++ )
        level8x8[ i8x8 ][ i ] = 0
}

 

   語法元素

  • residual_luma  亮度殘差變換量化后的語法結構,各參數說明如下
    • i16x16DClevel 如果是intra16x16的宏塊模式,會分開AC與DC單獨編碼,DC level會從residual_block內取出,並存在i16x16DClevel數組內
    • i16x16AClevel 如果是intra16x16的宏塊模式,會分開AC與DC單獨編碼,AC level會從residual_block內取出,並存在i16x16AClevel數組內
    • level4x4  不是intra16x16的情況下,如果DCT變換采用的是4x4的方式,宏塊的level會從residual_block內取出,並存在level4x4數組內
    • level8x8  不是intra16x16的情況下,如果DCT變換采用的是8x8的方式,宏塊的level會從residual_block內取出,並存在level8x8數組內
    • startIdx  需要進行熵編碼的數組起始位置的索引
    • endIdx  需要進行熵編碼的數組結束位置的索引
  • residual_block  這並不是完整的熵編碼,而是熵編碼參數的語法結構,以后分析熵編碼再進行分析

 

luma level分為幾種,如上面的數組i16x16DClevel, i16x16AClevel, level4x4, level8x8,在解碼過程中,這些數組后續會被用於逆量化、逆變換,residual_luma的目的是從residual_block內取出level,並填充到這些數組當中。

我們來完整分析一下residual_luma的工作過程

  • 如果當前宏塊是Intra_16x16的預測模式,並且為亮度殘差語法結構的開頭,那么會從residual_block中獲取16個DC level,存儲到i16x16DClevel數組內
  • 接下來會以子宏塊為單位進行,一個宏塊內有4個8x8大小的子宏塊
    • 如果當前宏塊是以4x4塊為單位進行DCT變換的(transform_size_8x8_flag=0),或者如果要求當前視頻使用CAVLC進行熵編碼(entropy_coding_mode_flag=0),這意味着熵編碼是以4x4塊為單位進行的。一個子宏塊內包含4個4x4個子塊。
      • 在當前4x4塊內level不全為0的情況下(CBP相應位為1),才需要從residual_block中獲取level。
        • 如果當前的宏塊為Intra_16x16的預測方式,需要從residual_block中獲取15個AC level,存儲到i16x16AClevel中
        • 否則,則會從residual_block中獲取16個level,存儲到level4x4中
      • 否則(CBP相應位為0),如果當前的宏塊為Intra_16x16的預測方式,它的AC level全為0,為i16x16AClevel中的15個元素賦值0
      • 否則(CBP相應位為0),意味着該4x4塊內的level全為0,就為level4x4中的16個元素賦值0
      • 如果當前視頻使用CAVLC進行熵編碼(entropy_coding_mode_flag=0),但是當前宏塊是以8x8塊為單位進行DCT變換的(transform_size_8x8_flag=1),我們則需要把這些CAVLC解碼得到的4x4塊組合成8x8塊,以便后續使用
    • 否則(要求當前視頻使用CABAC進行熵編碼(entropy_coding_mode_flag=1)並且當前宏塊是以8x8為單位進行DCT變換(transform_size_8x8_flag=1),如果該8x8塊內的level不全為0(CBP相應位為1),那么就從residual_block中獲取64個level,並存儲到level8x8中)
    • 否則(要求當前視頻使用CABAC進行熵編碼(entropy_coding_mode_flag=1)並且當前宏塊是以8x8為單位進行DCT變換(transform_size_8x8_flag=1),如果該8x8塊內的level全為0(CBP相應位為0),就為level8x8中的64個元素賦值0.

 

 

如果沒有宏塊CBP上的bit全都不為0的話,它的residual就會是如下的樣子

image


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM