三、slice頭相關(guān)的一些細節(jié)
1.關(guān)于POC的計算
圖像序列號(POC)主要用于標(biāo)識圖象的播放順序,同時還用于在對幀間預(yù)測片解碼時,標(biāo)記參考圖像的初始圖像序號。 對于每個編碼幀有兩個圖像序列號,分別稱為頂場序列號(TopFieldOrderCnt)和底場序列號(BottomFieldOrderCnt );對于每個編碼場有一個圖像序列號,對于一個編碼頂場其稱為TopFieldOrderCnt,對于編碼底場,其稱為 BottomFieldOrderCnt ;對于每個編碼場對有兩個圖像序列號,TopFieldOrderCnt 和BottomFieldOrderCnt 分別用于標(biāo)記該場對的頂場和底場。 opFieldOrderCnt 和BottomFieldOrderCnt 分別指明了相應(yīng)的頂場/ 底場相對于前一個IDR 圖像,或解碼順序中前一個包含memory_management_control_operation=5的參考圖像(此值為5表示清空參考幀隊列,因 此有著跟IDR同樣的效果),的第一個輸出場的相對位置。這也意味著,只要遇到IDR圖像或 memory_management_control_operation=5的參考圖像,相應(yīng)的POC就等于零。 在H.264中,由于B幀可以進行雙向預(yù)測,因此圖像的解碼順序可以不同于播放順序。 前面已經(jīng)提到,計算POC(也就是計算TopFieldOrderCnt和BottomFieldOrderCnt),有三種方法,具體使用哪種由序列參數(shù)集中的pic_order_cnt_type元素指定。下面一次介紹三種方法。 (1) pic_order_cnt_type=0 本過程的輸入是在本節(jié)規(guī)定的解碼順序中前一圖像的PicOrderCntMsb,也即prevOrderCntMsb 。 關(guān)于Msb:POC由高位Msb和低位Lsb兩部分組成,當(dāng)Lsb發(fā)生溢出時,會向Msb進位,Msb+Lsb=POC。 本過程的輸出是TopFieldOrderCnt 和BottomFieldOrderCnt ,或者其中之一。
大致流程:首先計算變量 prevPicOrderCntMsb,然后計算當(dāng)前圖像的PicOrderCntMsb (剛剛提到POC=Msb+Lsb,Lsb已經(jīng)在碼流的slice_header中由pic_order_cnt_lsb指定,因此主要任務(wù)其實就是計算 Msb),最后計算當(dāng)前圖像的TopFieldOrderCnt 和(或) BottomFieldOrderCnt。
流程圖:
其中,MaxPicOrderCntLsb由序列參數(shù)集中的log2_max_pic_order_cnt_lsb_minus4元素確定;delta_pic_order_cnt_bottom在片頭中指定,上文介紹POC時已介紹過。 圖中所提到的“亂序”,指的是,解碼順序是否與播放順序不一致,準(zhǔn)確的說,如果當(dāng)前圖像的播放順序POC比上一個解碼圖像的播放順序prePOC小,就是 發(fā)生了亂序。那如何判斷是否發(fā)生亂序了呢?一般情況下,Msb在解碼順序相鄰的兩個圖片間不發(fā)生變化時(即Lsb不發(fā)生溢出或借位),只要判斷當(dāng)前解碼的 Lsb是否比之前解碼的Lsb小即可,如果比之前的小,說明亂序發(fā)生;但是如果Lsb發(fā)生溢出或借位時,就不能這樣簡單的判斷了(前后兩幀的Msb將不再 相同),那如何判斷Lsb是否發(fā)生了溢出或借位呢?這就要用到一個規(guī)則:解碼順序相鄰的兩個圖像,他們的播放順序POC之差(的絕對值)不會超過MaxPicOrderCntLsb / 2,根據(jù)這個規(guī)則,計算解碼順序相鄰的兩幅圖像的Lsb之差,并與MaxPicOrderCntLsb/2進行比較,就可判斷Lsb是否發(fā)生了溢出或借位。在標(biāo)準(zhǔn)中,關(guān)于Msb的計算是這樣描述的:
if( ( pic_order_cnt_lsb < prevPicOrderCntLsb ) && ( ( prevPicOrderCntLsb ? pic_order_cnt_lsb ) >= ( MaxPicOrderCntLsb / 2 ) ) ) PicOrderCntMsb = prevPicOrderCntMsb + MaxPicOrderCntLsb else if( ( pic_order_cnt_lsb > prevPicOrderCntLsb ) && ( ( pic_order_cnt_lsb ? prevPicOrderCntLsb ) > (MaxPicOrderCntLsb / 2 ) ) ) PicOrderCntMsb = prevPicOrderCntMsb ? MaxPicOrderCntLsb (這一步亂序發(fā)生) else PicOrderCntMsb = prevPicOrderCntMsb
(1) pic_order_cnt_type=1 本過程的輸入是在本節(jié)規(guī)定的解碼順序中前一圖像的FrameNumOffset。 本過程的輸出是TopFieldOrderCnt 和BottomFieldOrderCnt ,或者其中之一。 計算過程中涉及到兩個變量prevFrameNum 和prevFrameNumOffset ,其中prevFrameNum 是前一圖像的frame_num ,而對于prevFrameNumOffset ,如當(dāng)前圖像不是IDR ,而前一圖像的memory_management_control_operation等于5 ,prevFrameNumOffset 設(shè)為0;否則,prevFrameNumOffset 設(shè)置等于前一圖像的FrameNumOffset;注意,當(dāng)序列參數(shù)及中的gaps_in_frame_num_value_allowed_flag 等于1 時(表示相鄰解碼圖像的frame_num可以出現(xiàn)間隔),通過frame_num 間隔的解碼過程可能會推斷出解碼順序中的前一幅圖像為“不存在”幀。 大致流程: 畢厚杰書中的插圖并沒有完全解釋清楚,有很多細節(jié)沒有說明,看了這個圖還是感覺什么都沒看懂,所以這部分還是結(jié)合標(biāo)準(zhǔn)中的說明來看比較好,但標(biāo)準(zhǔn)中只是說了每個值該如何計算,而沒有詳細講解,因此要想搞懂每一步的意義也要費點腦細胞才行。 a.關(guān)于absFrameNum:可以理解成“絕對幀序號”,而原來的frame_num則應(yīng)理解為相對幀序號。前面講frame_num時提到, 當(dāng)一個序列中的參考幀數(shù)量超過MaxFramenum時,frame_num在達到MaxFramenum后會重新從0開始循環(huán)計數(shù),這樣的話,一個序列 中可能會存在兩個或多個參考圖像擁有相同的“相對幀序號(即frame_num)”的情況 。因此,如果需要一個符號來唯一地標(biāo)識一個序列中的所有參考幀,用相對幀序號是不行的,于是就需要為每個參考幀分配一個絕對幀序 號:absFrameNum=FrameNumOffset + frame_num,F(xiàn)rameNumOffset代表當(dāng)前序列中frame_num已經(jīng)循環(huán)的次數(shù)與MaxFrameNum的積。 absFrameNum的具體計算過程如下: if(num_ref_frames_in_pic_order_cnt_cycle != 0 ) absFrameNum = FrameNumOffset +frame_num else absFrameNum = 0 if( nal_ref_idc = = 0 && absFrameNum > 0 ) absFrameNum = absFrameNum ? 1
其中,num_ref_frames_in_pic_order_cnt_cycle在序列參數(shù)集中指定,其取值范圍[0,255],它是數(shù)組 offset_for_ref_frame[]的變化周期(或者說數(shù)組長度),這個數(shù)組也是在序列參數(shù)集中指定的,這個周期和這個數(shù)組到底什么意思呢?在 用第二種POC計算方法時,一個參考幀跟下一個參考幀,他們POC的差值不能是任意的,必須是周期變化的,即每隔 num_ref_frames_in_pic_order_cnt_cycle個參考幀,相鄰參考幀之間POC的差值循環(huán)一次,而每個周期中,第i個差值 即為offset_for_ref_frame[i],正是這個規(guī)律,才使得第二種POC算法變得可行,在步驟c、d中將會看到這個數(shù)組的作用。 nal_ref_idc = = 0 表示當(dāng)前圖像不是參考圖像。當(dāng)當(dāng)前圖像不是參考圖像時,absFrameNum要額外減1。
b. picOrderCntCycleCnt 和 frameNumInPicOrderCntCycle 這倆分別代表absFrameNum對num_ref_frames_in_pic_order_cnt_cycle(前面說到的周期值)取模和取余。當(dāng) absFrameNum 大于0 時,二者的值由如下方法得到: if(absFrameNum > 0 ) { picOrderCntCycleCnt=(absFrameNum? 1 ) / num_ref_frames_in_pic_order_cnt_cycle ; //得到完整周期數(shù)。 frameNumInPicOrderCntCycle=(absFrameNum?1)%num_ref_frames_in_pic_order_cnt_cycle; //得到最后一個不完整的周期中參考幀的數(shù)量 }
c.關(guān)于expectedDeltaPerPicOrderCntCycle:代表每隔一個完整周期,POC總的變化量。求這個值只需將上面說到的數(shù)組中各個元素相加即可。 expectedDeltaPerPicOrderCntCycle = 0 for( i = 0; i <num_ref_frames_in_pic_order_cnt_cycle; i++ ) expectedDeltaPerPicOrderCntCycle += offset_for_ref_frame[ i ]
d.關(guān)于expectedPicOrderCnt:期望的POC值。要得到這個值,只需用POC在一個完整周期內(nèi)的總變化量乘以周期數(shù),再加上最后一個不完整周期跨越的POC數(shù)量即可。 if(absFrameNum > 0 ){ expectedPicOrderCnt=picOrderCntCycleCnt* expectedDeltaPerPicOrderCntCycle for( i = 0; i <= frameNumInPicOrderCntCycle; i++ ) //循環(huán)計算最后一個不完整周期所跨越的POC數(shù)量。 expectedPicOrderCnt = expectedPicOrderCnt + offset_for_ref_frame[ i] } else expectedPicOrderCnt = 0 if( nal_ref_idc = = 0 ) //對于非參考圖像 expectedPicOrderCnt = expectedPicOrderCnt + offset_for_non_ref_pic 其中,offset_for_ref_frame[ i ](前面已經(jīng)說過)和offset_for_non_ref_pic都是在序列參數(shù)集中指定,他們的取值范圍都是[-2^31,2^31-1]。
e.變量TopFieldOrderCnt 或 BottomFieldOrderCnt 的值由如下方法得到: if( !field_pic_flag ) { //當(dāng)前圖像不是場圖像 TopFieldOrderCnt = expectedPicOrderCnt + delta_pic_order_cnt[ 0 ] BottomFieldOrderCnt = TopFieldOrderCnt + offset_for_top_to_bottom_field + delta_pic_order_cnt[ 1 ] } else if( !bottom_field_flag ) TopFieldOrderCnt = expectedPicOrderCnt + delta_pic_order_cnt[ 0 ] else BottomFieldOrderCnt = expectedPicOrderCnt + offset_for_top_to_bottom_field +delta_pic_order_cnt[ 0 ]
其中,offset_for_top_to_bottom_field在序列參數(shù)集中指定,delta_pic_order_cnt[ 0或1 ]在片頭指定。 補充:從上面的過程中可以看到,當(dāng)圖像序列中出現(xiàn)兩個或多個連續(xù)的非參考幀時,這些非參考幀將具有相同的POC期望值(即 expectedPicOrderCnt),但是他們的頂場序號和底場序號可以通過各自的delta_pic_order_cnt[0和1 ]加以區(qū)別。因此第二種POC計算方法也是支持圖像序列中出現(xiàn)連續(xù)非參考幀的。而下面將介紹的第三種POC計算方法則不支持連續(xù)的非參考幀。
(1) pic_order_cnt_type=2 第三種POC計算方法所依賴的額外參數(shù)最少(可以節(jié)省片頭的比特數(shù)),它只根據(jù)frame_num就可以得到頂、底場的序列號。但是缺點是,用這種方法時,不允許圖像序列中出現(xiàn)連續(xù)的非參考幀。 大致流程:
這個方法不像第二種那么麻煩,從圖示中已可以看出完整的過程。唯一需要說的一點是,最后計算頂、底場序號時的規(guī)則(這個規(guī)則在三種方法中各不相同): if(!field_pic_flag ) { TopFieldOrderCnt = tempPicOrderCnt BottomFieldOrderCnt = tempPicOrderCnt } else if(bottom_field_flag ) BottomFieldOrderCnt = tempPicOrderCnt else TopFieldOrderCnt = tempPicOrderCnt
在這種方法中,如果解碼順序相鄰的兩個幀具有相同的frame_num,那么其中必有一個是參考幀,而另一個則是非參考幀,而且參考幀總是在非參考幀之前進行編解碼(和傳輸),但非參考幀比參考幀先采樣和播放(即非參考幀的POC更靠前)。
|
|