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文件都沒有包含這部分。
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
……
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
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內頭部的對齊外,它由宏塊信息循環組合而成。
語法元素
- 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內描述,它也是宏塊的一種結構。
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
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
圖例中,結構上面的數字代表了該語法元素所屬的子宏塊。
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塊,以便后續使用
- 在當前4x4塊內level不全為0的情況下(CBP相應位為1),才需要從residual_block中獲取level。
- 否則(要求當前視頻使用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.
- 如果當前宏塊是以4x4塊為單位進行DCT變換的(transform_size_8x8_flag=0),或者如果要求當前視頻使用CAVLC進行熵編碼(entropy_coding_mode_flag=0),這意味着熵編碼是以4x4塊為單位進行的。一個子宏塊內包含4個4x4個子塊。
如果沒有宏塊CBP上的bit全都不為0的話,它的residual就會是如下的樣子