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