JM86 中 POC 的計算方法



來自:http://hi.baidu.com/snowxshy/item/8fcad08318b86adfd1f8cdcb

一·參數說明

這一節闡述的是 encoder.cfg 中的參數對編碼過程的影響
要注意的是 encoder.cfg 中的參數跟 input 結構體中的變量是一一對應的

StartFrame:從視頻流的第幾幀開始編碼
FramesToBeEncoded:指明瞭除去 B 幀後將要被編碼的幀數
input->no_frames = FramesToBeEncoded
FrameSkip:指明瞭編碼過程中跳過的幀數,中間有 B 幀也算跳過一幀。
NumberBFrames:相鄰 I、P 幀或相鄰的 P 幀之間的 B 幀個數,必須有
     NumberBFrames< FrameSkip
     input->successive_Bframe = NumberBFrames
IntraPeriod:I 幀出現的頻率。若 IntraPeriod=3,則每 3 幀(不含 B 幀)中有一 I 幀;
                                                          IntraPeriod=0 時只有第一幀是 I 幀。
IDRIntraEnable:此值爲 1 時每個 I 幀都是 IDR,否則只有第一個 I 幀是 IDR。

舉例:在 StartFrame=0
                  FramesToBeEncoded=5
                  FrameSkip=3
                  NumberBFrames=2
                  IntraPeriod=3
                  IDRIntraEnable=1
            的情況下編碼情況如下,其中紅色代表 IDR 幀  
表 1
視頻流    0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
編碼流    I B B    P B B    P B B      I   B   B      P   
編碼順序 0 2 3    1 5 6    4 8 9      7 11 12      10   


二·pic_order_cnt_type 爲 0 的情況
這種情況下顯式的計算 POC
(1) 編碼端 I 幀或 P 幀 toppoc 的計算
這個過程在 main()函數的組循環
      “for (img->number=0; img->number < input->no_frames; img->number++){ }”
中實現
   IntraPeriod 或 IDRIntraEnable 爲零時
這種情況下只有第一個 I 幀是 IDR 幀,比較簡單。對於 I 幀或 P 幀,其頂場的 POC 爲
                    (img->number) * (2*(input->successive_Bframe+1))

IntraPeriod 和 IDRIntraEnable 都不爲零時
這種情況下每個 I 幀都是 IDR 幀,其 POC 必須設置爲零,I 幀出現的頻率爲 IntraPeriod,
故其 toppoc 爲
        (img->number % input->intra_period) * (2*(input->successive_Bframe+1))
z    說明:
原程序中使用了宏定義 IMG_NUMBER
      “#define IMG_NUMBER (img->number - start_frame_no_in_this_IGOP)”
通過搜start_frame_no_in_this_IGOP 可知這個變量在NumberOfFrameInSecondIGOP 爲0
(encoder_main.cfg 中就是這樣設置的)時恆爲 0,故有
                      IMG_NUMBER = img->number

(2) 編碼端 B 幀 POC 的計算
由表一可知,在編完一 I 幀或 P 幀之後纔開始對它前面的 B 幀進行編碼

    for (img->number=0; img->number < input->no_frames; img->number++)
    {
        ……I,P 幀編碼……
        if ((input->successive_Bframe != 0) && (IMG_NUMBER > 0))  
{
      ……
      for(img->b_frame_to_code=1; img->b_frame_to_code<=input->successive_Bframe;      
                                                                                                    img->b_frame_to_code++)
      {
      }

    IntraPeriod 或 IDRIntraEnable 爲零時 toppoc 等於
                2+(img->number-1) * (2*(input->successive_Bframe+1))  
                  +2* (img->b_frame_to_code-1)
a)    第一個 2 指得是 IDR 的兩個場;
b)    img->number 要減一是因爲要對當前幀(img->number)前面的 B 幀進行編碼;

    IntraPeriod 和 IDRIntraEnable 都不爲零時 toppoc 等於
      2+(img->number % input->intra_period-1) * (2*(input->successive_Bframe+1))+2* (img->b_frame_to_code-1)
IDR 幀前面


(3) toppoc 到 pic_order_cnt_lsb 的轉化
                img->pic_order_cnt_lsb
              =img->toppoc &
                ~((((unsigned int)( –1)) << (log2_max_pic_order_cnt_lsb_minus4+4)))

    (unsigned int)(-1)的十六進制形式是 0xffffffff,即它的每一位都是 1;
    log2_max_pic_order_cnt_lsb_minus4+4 是圖象數目(包括 B 幀)最大值的位數

當 toppoc >0 時,img->pic_order_cnt_lsb=img->toppoc
當 toppoc <0 時,img->pic_order_cnt_lsb= max_pic_order_cnt+ img->toppoc
其中 max_pic_order_cnt=1<<( log2_max_pic_order_cnt_lsb_minus4+4)
   疑問:
不知道 toppoc 到 pic_order_cnt_lsb 這個過程有什麼意義;
POC 的值會從 0 變到很大,爲什麼不對它進行熵編碼;


(4) 解碼端 toppoc 的恢復
        此過程在函數 decode_poc 中執行。其思想是對於 IDR 前的 B 幀
                      Toppoc = pic_order_cnt_lsb - max_pic_order_cnt
否則  
                            Toppoc = pic_order_cnt_lsb
是否減去 max_pic_order_cnt 由變量 PicOrderCntMsb 決定,對於 IDR 前的 B 幀
                      PicOrderCntMsb = (– max_pic_order_cnt)
否則
                      PicOrderCntMsb = 0
到這就不難理解 PicOrderCntMsb 的含義了,PicOrderCntMsb 反映了 toppoc 的值是否小於 0。
至於另外兩個參數:PrevPicOrderCntMsb 總是爲 0;PrevPicOrderCntLsb 在當前圖象是 IDR
或 IDR 前(視頻流中)的 B 幀時爲 0,否則等於前一圖象(編碼序列中)的 PicOrderCntLsb。


三·pic_order_cnt_type 爲 1 的情況
這種情況下通過 frame_num 來計算 POC
(1)frame_num 簡介
參考《畢厚傑》7.3.4 節中 frame_num 條款的解釋,對於表 1 中的圖象序列,其 frame_num的值參考如下:

表 2
視頻流      0   1   2   4   5      6      8      9     10    12    13    14 16
編碼流      I   B B P   B      B      P      B      B      I      B     B      P
編碼順序    0   2   3   1   5      6      4      8      9   7     11    12 10
frame_num   0   2   2   1   3      3      2      1      1   0      2      2      1
poc         0   2   4   6   8     10    12    -4     -2      0      2      4      6

(2)算法思想以及其解碼端的實現
    對於 IDR 幀,poc = 0;
    對於 I 幀或 P 幀
            poc = frame_num*2*(input->successive_Bframe+1)
或    
            poc = 2*(input->successive_Bframe+1)
                      + (frame_num – 1)*2*(input->successive_Bframe+1)

解碼端實現
            poc = img->ExpectedPicOrderCnt
                      + img->delta_pic_order_cnt[0]      (在 I,P 幀下爲 0)
z    對於 I 幀或 P 幀之前的 B 幀(視頻流中)
            poc = (frame_num – 1)*2*(input->successive_Bframe+1)
                      – 2*(input->successive_Bframe+1 – img->b_frame_to_code)

            poc = 2*(input->successive_Bframe+1)
                      + (frame_num – 1 – 1)*2*(input->successive_Bframe+1)
                      + 2*( img->b_frame_to_code – 1)
                      – 2*input->successive_Bframe
解碼端的實現
            poc = img->ExpectedPicOrderCnt
                      + img->delta_pic_order_cnt[0]
                      + active_sps->offset_for_non_ref_pic

   變量說明
a)    其中 img->b_frame_to_code 請參見標題一·(2)
b)    img->disposable_flag = (nalu->nal_reference_idc = = 0),而 nal_reference_idc 只在 B 幀時爲0,即img->disposable_flag 只在B 幀時爲1。這也是在B 幀情況下img->AbsFrameNum
要比 I 幀或 P 幀多減去一個 1 的原因。
c)    其它變量參見下面小題;


(3)編碼端參數設置
a)    img->num_ref_frames_in_pic_order_cnt_cycle:
                這個參數在 init_poc( )函數中設置爲 1 後就再沒改動過;
b)    img->offset_for_ref_frame[0] :
                在 StoredBPictures 爲 0 時等於 2*(input->successive_Bframe+1);
c)    img->offset_for_ref_frame[1] :
                沒什麼用,264 頭文件中不會保存此變量;
d)    img->delta_pic_order_cnt[0]    :
                這個變量只對 B 幀有用,等於 2*(img->b_frame_to_code –1); 對於 I 幀或 P 幀, 其值爲 0;
e)    active_sps->offset_for_non_ref_pic:
                只對 B 幀有用,在 StoredBPictures 爲 0 時等於–2*input->successive_Bframe,

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