從Slice_Header學習H.264

從Slice_Header學習H.264

 

寫在前面

$     H.264我是結合標準和畢厚傑的書一塊學的。看句法語義時最是頭疼,一大堆的元素,很需要耐心。標準中在介紹某個元素的語義時,經常會突然冒出與之相關的另一個變量,這個變量一般都在前文中講過,但那麼多變量怎麼可能看一遍就記住?這時我只能去前面重新找這個變量再看一遍。沒辦法,H.264這個龐大的結構內部肯定是環環相扣的,各個部分聯繫緊密,所以剛開始看時要搞明白H.264的主要細節以及相互間的關係不是特別容易,尤其看到一大堆不認識的變量時,頭大是難免的。所以做這個筆記時,介紹語法元素的過程中我特意將所有被牽扯到的其他變量都順帶解釋一下,並記錄下來他們是在哪個結構中出現的,這樣方便大家也方便以後我自己翻閱。

 

$     對於序列參數集和圖像參數集,初看時會發現這兩個參數集中的很多元素不能立刻搞明白,這是因爲很多細節性的東西你還沒有了解,這些搞不明白的元素大都會在下面介紹片頭語法元素的過程中被提到並解釋,我們逐漸進入細節。不過在這之前,也要簡單看一下兩個參數集,最起碼要了解他們的作用,以及把其中不涉及細節的那部分元素儘可能搞明白。

 

$     要了解H.264的全局結構以及一些重要細節,個人感覺slice_header是一個很好的切入點。這一系列筆記都是結合片頭結構敘述的,前後會逐漸涉及到很多內容。標準中在介紹一些細節操作時,通常是隻把計算方法用一大堆僞代碼寫出來,卻沒有直白的描述,要看明白很費勁;畢厚傑書中的插圖可以幫大忙,結合圖來看會容易一下,但畢厚傑的書中有些細節內容沒有出現(估計是讓讀者自己看標準),而且有些不易搞懂的地方畢書中也是直接照搬的標準。本系列筆記中,我把我剛開始看時容易疑惑的部分按照我現在的理解重新描述了一遍,有些必要的地方也會照搬標準的僞代碼,不過在照搬之前我會先做盡可能直白的描述,說明僞代碼要幹什麼。我儘可能地將涉及到的所有細節都敘述到。

 

$     個人感覺初學時可以先不深究冗餘片、濾波等,大概有個概念就行,否則由於搞不懂可能會越看越煩躁,降低學習效率,數據分割如果暫時不是特別清楚也可以先隔過去,這些都可以等對h.264比較熟悉後再回來看,那時也許就感覺容易多了。多片組將在本系列筆記的最後說到,所以中間遇到多片組時可以先把疑惑放一邊。

 

 

一、slice頭的主要元素介紹

 

       首先做一下說明,slice_header()如果存在,那片頭中的語法元素 pic_parameter_set_id 、 frame_num、 field_pic_flag、bottom_field_flag、 idr_pic_id、 pic_order_cnt_lsb 、 delta_pic_order_cnt_bottom 、delta_pic_order_cnt[ 0 ] 、delta_pic_order_cnt[ 1 ] 、 sp_for_switch_flag 和 slice_group_change_cycle在同一個圖像的所有條帶頭(條帶=片=slice,條帶頭=片頭=slice_header)中都應有相同的值。下面開始介紹各個元素。


$     slice_type           so easy,略過

$     pic_parameter_set_id          so easy,略過

 

$     片頭的field_pic_flag  ,指定當前圖像是幀編碼(0)還是場編碼(1)。這個元素在同一圖像的所有片中應具有相同值。僅當序列參數集中的frame_mbs_only_flag爲0時,這個元素纔會存在在碼流中。

       序列參數集中的句法元素frame_mbs_only_flag 和mb_adaptive_frame_field_flag再加上本句法元素共同決定圖像的編碼模式。

frame_mbs_only_flag

mb_adaptive_frame_field_flag

field_pic_flag

模式

1

不存在於碼流中

不存在於碼流中

幀編碼

0

0

0

幀編碼

0

0

1

場編碼

0

1

0

幀場自適應(僅在此情形下,MbaffFrameFlag=1,

其他幾種情況下MbaffFrameFlag都爲0)

0

1

1

場編碼

 

$     first_mb_in_slice   表示本片中第一個宏塊的地址。

 (MbaffFrameFlag的取值參考上面的表格)

如果 MbaffFrameFlag 等於0,first_mb_in_slice就是該條帶中第一個宏塊的地址,並且first_mb_in_slice 的值應在0到 PicSizeInMbs– 1 的範圍內(包括邊界值)。

否則,first_mb_in_slice * 2  就是該條帶中的第一個宏塊地址,該宏塊是該條帶中第一個宏塊對中的頂宏塊,並且first_mb_in_slice 的值應該在 0 到 PicSizeInMbs/ 2 – 1 的範圍內(包括邊界值)。

其中,MbaffFrameFlag由序列參數集中的mb_adaptive_frame_field_flag指定,它等於1時表示使用幀場自適應模式,否則不使用;PicSizeInMbs表示圖像的大小(以宏塊爲單位),由序列參數集中的pic_width_in_mbs_minus1、pic_height_in_map_units_minus1以及其他一些元素指定(這裏就不詳細說明了,因爲這涉及到映射單元與宏塊的對應關係,這到後面會說到)。


$     bottom_field_flag    指定當前的場是頂場還是底場。等於1 時表示當前圖像是屬於底場;等於 0 時表示當前圖像是屬於頂場。這個元素僅當field_pic_flag存在且爲1時(說明當前片屬於一個場圖像),纔會出現在碼流中。

 

$     frame_num和PicNum(picnum不是slice頭的元素)

對於非參考幀來說,它的frame_num 值在解碼過程中是沒有意義的,因爲frame_num 值是參考幀特有的,它的主要作用是在該圖像被其他圖像引用作運動補償的參考時提供一個標識。但 H.264 並沒有在非參考幀圖像中取消這一句法元素,原因是在 POC 的第二種和第三種解碼方法中可以通過非參考幀的frame_num 值計算出他們的POC 值。

       frame_num是對幀編號的,也就是說如果在場模式下,同屬一個場對的頂場和底場兩個圖像的frame_num 的值是相同的。

frame_num 是參考幀的標識,但是在解碼器中,並不是直接引用的 frame_num 值,

而是由frame_num  進一步計算出來的變量 PicNum。MaxPicNum表徵PicNum的最大值,

在場模式下MaxPicNum=2*MaxFrameNum,否則MaxPicNum =MaxFrameNum。其中,MaxFrameNum 由序列參數集中的log2_max_frame_num_minus4 確定。PicNum 和frame_num 一樣,也是嵌在循環中,當達到這個最大值時,PicNum將從0 開始重新計數。

       CurrPicNum是當前圖像的PicNum 值,在計算PicNum的過程中,當前圖像的 PicNum 值是由frame_num 直接算出:

              -  如果field_pic_flag= 0 ,  CurrPicNum = frame_num.

-  否則, CurrPicNum= 2 * frame_num + 1.

 

$     序列參數集中的gaps_in_frame_num_value_allowed_flag等於0時,參考幀的frame_num都是連續的;如果等於1,這時若網絡阻塞,編碼器可以將編碼後的若干圖像丟棄,而不用另行通知解碼器。在這種情況下,解碼器必須有機制將缺失的frame_num 及所對應的圖像填補,否則後續圖像若將運動矢量指向缺失的圖像將會產生解碼錯誤。

 

$     idr_pic_id    IDR  圖像的標識。不同的 IDR 圖像有不同的idr_pic_id 值.在場模式下,IDR幀的兩個場有相同的idr_pic_id值。idr_pic_id 的取值範圍是 [0,65535],超出此範圍時,以循環的方式重新開始計數。

 

$     POC相關:

       POC是指pic_order_cnt ,表示圖像的播放順序。POC的有三種計算方法,具體使用哪一種算法來計算POC,是由序列參數集中的pic_order_cnt_type指定的。在 POC 的第一種算法中是顯式地傳遞POC 的值,而其他兩種算法是通過frame_num 來映射POC 的值。三種算法下POC具體如何計算是在 標準2005/03的“8.2.1  圖像順序號的解碼過程”章節中講述的。

       pic_order_cnt_lsb:當序列參數集中的pic_order_cnt_type等於0時,本元素將出現在碼流中。在POC 的第一種算法中,本元素“顯式地傳遞了POC值”,準確的說,是POC值的lsb(具體參見標準8.2.1.1)。序列參數集中的log2_max_pic_order_cnt_lsb_minus4元素指定了編碼pic_order_cnt_lsb的最大比特數。

       delta_pic_order_cnt_bottom:此元素用於POC的第一種算法。當序列參數集中的frame_mb_only_flag  不爲 1時(圖像序列中既可以有場圖像又可以有幀圖像),幀或幀場自適應圖像中包含的兩個場也必須有各自的 POC 值(供後續場圖像作爲參考圖像)。通過此元素可在已解碼的幀或幀場自適應圖像的 POC 基礎上新映射一個POC 值,並把它賦給底場。本元素存在條件:序列參數集中的pic_order_cnt_type等於0(使用第一種算法計算POC)、圖像參數集中的pic_order_present_flag等於1(表示與圖像順序數有關的語法元素將出現於條帶頭中)、 片頭的field_pic_flag存在且爲0。

       delta_pic_order_cnt[  0 ], delta_pic_order_cnt[ 1 ]:這兩個語法元素功能與delta_pic_order_cnt_bottom類似,只不過這兩個元素用於POC的第二、三中算法(這裏有點疑問,因爲標準的語法表中,這兩個元素僅在pic_order_cnt_type=1,即使用第二種POC算法時纔出現,這意味着使用第三種POC算法的話,這倆元素就不存在了,既然不存在,還怎麼用於第三種算法呢)。POC 的第二和第三種算法是從frame_num映射得來。delta_pic_order_cnt[0 ]的存在條件:序列參數集中的delta_pic_order_always_zero_flag等於0(等於1表示視頻序列的條帶頭中沒有delta_pic_order_cnt[0 ]  和delta_pic_order_cnt[ 1 ]  兩個字段,它們的值都默認爲0)、且pic_order_cnt_type  = 1(使用第二種POC算法);delta_pic_order_cnt[1 ]的存在條件:在delta_pic_order_cnt[  0 ]存在條件的基礎上,圖像參數集中的pic_order_present_flag等於1(表示與圖像順序數有關的語法元素將出現於條帶頭中)、 片頭的field_pic_flag存在且爲0。

       強調:上面提到圖像參數集中的pic_order_present_flag等於1表示“與圖像順序數有關的語法元素將出現於條帶頭中”,但這個pic_order_present_flag並不是對所有與圖像順序相關的語法元素起作用,他只對條帶(片)頭中的delta_pic_order_cnt_bottom和delta_pic_order_cnt[ 1 ]起作用,而pic_order_cnt_lsb和delta_pic_order_cnt[ 0]則不受其制約。

 

$     redundant_pic_cnt         對於屬於基本編碼圖像的條帶和條帶數據隔離帶應等於0。對於一個冗餘編碼圖像的編碼條帶或編碼條帶數據隔離帶的redundant_pic_cnt  的值應大於0。當redundant_pic_cnt  在比特流中不存在時,應推定其值爲0。redundant_pic_cnt  的值應該在0 到127 範圍內;每個冗餘編碼圖像都有一個對應的基本編碼圖像;對於冗餘編碼圖像的編碼條帶(或數據分割),其通過pic_parameter_set_id指定的圖像參數集,必須與對應的基本編碼圖像的編碼條帶指定的圖像參數集具有相同的pic_order_present_flag值;標準裏在介紹這個元素時用了將近一頁的篇幅,內容還挺多,不想過現在關於冗餘圖像的部分,還是先不要看了,越看腦子越亂,先大概知道有這麼回事兒,等以後對H.264熟悉了再回來看也許就容易多了。

元素存在條件:圖像參數集中的redundant_pic_cnt_present_flag等於1(表示redundant_pic_cnt  語法元素將出現在條帶頭、圖像參數集中指明(直接或與相應的數據分割塊 A 關聯)的數據分割塊B 和數據分割塊C 中)。對於數據分割,當這個條件符合時,不僅在A分割的碼流中會出現redundant_pic_cnt(並不是直接出現,而是包含在分割A的片頭結構中),還會在與之對應的B、C分割中也出現(直接出現)。

 

$     direct_spatial_mv_pred_flag  指出在B 圖像的直接預測的模式下,用時間預測還是用空間預測。1 :空間預測(標準8.4.1.2節給出的亮度運動矢量B_Skip、B_Direct_16x16 和

B_Direct_8x8  將使用空間指引的模式來預期);0:時間預測(亮度運動矢量B_Skip、B_Direct_16x16 和 B_Direct_8x8  將使用臨時指引的模式來預期)。

       存在條件:  片頭的slice_type =B ,即當前片是B片。

 

$     關於List0和List1中的參考幀數目。

       在圖像參數集中,已經通過num_ref_idx_l0_active_minus1和num_ref_idx_l1_active_minus1這兩個元素指明瞭參考幀數目,num_ref_idx_l0_active_minus1  表示參考圖像列表0 的最大參考索引號,該索引號將用來在一幅圖像中num_ref_idx_active_override_flag 等於0 的條帶使用列表0 預測時,解碼該圖像的這些條帶。當MbaffFrameFlag 等於1 時(幀場自適應),num_ref_idx_l0_active_minus1 是幀宏塊解碼的最大索引號值,而2 * num_ref_idx_l0_active_minus1 + 1 是場宏塊解碼的最大索引號值。(幀場自適應模式下,一個宏塊是幀宏塊還是場宏塊由slice_data語法中的mb_field_decoding_flag元素指定,每個宏塊都可以單獨指定自己的幀/場模式)。num_ref_idx_l0_active_minus1 的值應該在0 到31的範圍內。num_ref_idx_l1_active_minus1具有類似的含義及規則,它表示的是List1中的最大參考索引號。

       爲給某些特定圖像更大的靈活度,在條帶頭中,可以重載這兩個元素。

       num_ref_idx_active_override_flag:條帶頭中的這個元素用來決定是否對這兩個元素進行重載,如果重載,那麼條帶頭中將再次出現num_ref_idx_l0_active_minus1和num_ref_idx_l1_active_minus1這兩個元素,它們將覆蓋在圖像參數集中的值。

 

 

$     ref_pic_list_reordering()  參考幀重排序。這個語法項目嵌套在條帶頭中,是條帶頭的一個子項目。

 

$     pred_weight_table() 預測加權表格。這個語法項目嵌套在條帶頭中,是條帶頭的一個子項目。此項目存在條件:

1. 如果當前片是P片或SP片,即slice_type = P  | | slice_type = SP:

如果圖像參數集中的weighted_pred_flag爲1(表示在P 和SP條帶中應使用加權的預測),則pred_weight_table( )存在。

2. 如果當前片是B片,即slice_type= B:

如果圖像參數集中的weighted_bipred_idc == 1,則pred_weight_table( )存在。關於weighted_bipred_idc ,它等於 0 時表示B 條帶應該採用默認的加權預測;等於1 表示 B 條帶應該採用具體指明的加權預測,只有這個情況下pred_weight_table( )才存在;等於2 表示B 條帶應該採用隱含的加權預測。(疑問:“默認的”和“隱含的”,怎麼感覺一個意思呢?)。weighted_bipred_idc的值應該在0 到2 之間(包括0 和2)。

 

$     dec_ref_pic_marking() 解碼的參考圖像標識。這個語法項目嵌套在條帶頭中,是條帶頭的一個子項目。此項目存在條件:NAL單元中的nal_ref_idc不爲0。nal_ref_idc不爲0時,表示NAL單元中包含一個序列參數集,或一個圖像參數集,或一個參考圖像條帶,或一個參考圖像的條帶數據分割。由於本語法項包含在片頭中,因此當前的NAL肯定是片或片的數據分割,也就是說,這個語法項存在的條件是:當前的NAL包含的是一個參考圖像的條帶或條帶數據分割。

 

$     上面提到的這三個子語法項目將在後面詳細介紹。

 

$     cabac_init_idc  代表一個表格序號,用於CABAC計算過程,表示用於決定關聯變量的初始化過程中使用的初始化表格的序號,範圍0 到2。(不太懂沒關係,知道有這麼個東西即可,等學CABAC時自然會明白)。

元素存在條件:圖像參數集中的entropy_coding_mode_flag等於1 (表示採用cabac編碼)、並且slice_type  != I  &&  slice_type !=  SI(表示當前片不是I片或SI片)。

 

$     slice_qp_delta  指出在用於當前片的所有宏塊的量化參數的初始值,這個元素用於普通幀(非SI和SP的幀),他們量化時都是對預測殘差變換後的係數進行量化。QPY。

             SliceQPY = 26 + pic_init_qp_minus26 +slice_qp_delta  

QPY的範圍是  0 to 51 。代表的意義是量化間距。

H.264 中的量化參數是分圖像參數集、片頭、宏塊頭三層給出的,前兩層各自給出一個偏移值,這個句法元素就是片層的偏移。

其中,pic_init_qp_minus26位於圖像參數集中。

 

$     slice_qs_delta   與slice_qp_delta 的與語義相似,用在 SI 和SP中 (這兩種片都是直接對預測值和實際值進行變換後對係數進行量化,而不是對殘差值變換後的係數進行量化)。

QSY = 26 + pic_init_qs_minus26 + slice_qs_delta

QSY 值的範圍是0  到51 。

              其中,pic_init_qs_minus26位於圖像參數集中。

       與普通幀不同的是,在對SI和SP幀進行編解碼時,需要兩組量化係數(當然這兩組係數可以一樣):對預測重構塊係數進行量化的量化參數 SPQP 與對預測殘差係數進行量化的量化參數 PQP。噹噹前片是SI或SP片時,本元素對應的就是SPQP,而PQP對應的是slice_qp_delta。

       本元素存在條件:slice_type  = = SP  | |  slice_type = =  SI(當前片是SP片或SI片)。

 

$     sp_for_switch_flag  指出SP幀中的p 宏塊的解碼方式是否是switching 模式。什麼是switching 模式?好吧,我也不懂,先記錄一下,等具體學習SP幀的時候再考慮這個問題,現在沒有必要爲這個糾結太多。

       存在條件:slice_type  = =  SP(當前片是SP片)。

 

$     slice_group_change_cycle  這個用於多片組。當片組的類型是3, 4, 5(這三種類型的情況下,每個圖像都只包含兩個片組),由此句法元素和圖像參數集中的slice_group_change_rate_minus1,可獲得片組0中映射單元的數目。

關於映射單元,其具體涵義將在後面將FMO時提到,現在也可以先跳到該處瀏覽一下映射單元的定義。

片組0中映射單元的數目由下式得到。

MapUnitsInSliceGroup0 =  Min( slice_group_change_cycle *SliceGroupChangeRate, PicSizeInMapUnits )    

slice_group_change_cycle 由Ceil( Log2( PicSizeInMapUnits ÷ SliceGroupChangeRate + 1 ) ) 位比特表示。slice_group_change_cycle 值的範圍是0  到Ceil( PicSizeInMapUnits÷ SliceGroupChangeRate )。

其中,SliceGroupChangeRate可由slice_group_change_rate_minus1得到;

而PicSizeInMapUnits = PicWidthInMbs * PicHeightInMapUnits ,等號右邊的兩個數都由序列參數集中的pic_width_in_mbs_minus1和pic_height_in_map_units_minus1得到,具體參見標準的7.4中關於序列參數集語意的講解;Ceil(x)函數表示返回大於或者等於x的最小整數。

       元素存在條件:圖像參數集中的num_slice_groups_minus1> 0、圖像參數集中的slice_group_map_type是3、4或5。

 

$     濾波相關:

       H.264指定了一套算法可以在解碼器端獨立地計算圖像中各邊界的濾波強度進行濾波。除了解碼器獨立計算之外,編碼器也可以傳遞句法元素來干涉濾波強度。

       disable_deblocking_filter_idc:表示去塊效應濾波器的操作在經過條帶的一些塊邊緣時是否會被廢棄,並指定該濾波器針對哪個邊緣被廢棄。當條帶頭中不存在disable_deblocking_filter_idc 時,其值默認爲0。disable_deblocking_filter_idc 的值應該在0 到2 範圍內(包括0 和2)。元素存在條件:圖像參數集中的deblocking_filter_control_present_flag等於1(deblocking_filter_control_present_flag  equal to 1 specifies that a set of synt axelements controlling the characteristics of the deblocking filter is present inthe slice header.)。

 

       slice_alpha_c0_offset_div2: 給出用於增強  α 和 tC0的偏移值

FilterOffsetA = slice_alpha_c0_offset_div2 << 1   

slice_alpha_c0_offset_div2  值的範圍是 -6  到+6。

slice_beta_offset_div2: 給出用於增強 β和 tC0的偏移值

FilterOffsetB = slice_beta_offset_div2 << 1

slice_beta_offset_div2值的範圍是 -6  到+6。

       這兩個元素的存在條件:   disable_deblocking_filter_idc  !=  1。對於這幾個元素,知道他們是跟方塊濾波相關的就行了,等學習了方塊濾波,這些元素的含義自然會明白。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章