本文參考自http://wenku.baidu.com/link?url=ZPF0iSKzwLQg_8K02pnnd_-Zd6ISnsOGWsGYb98ucLkELZO4nOv-X-v2GKLzI3r0VMN4R0TC8cM6AQy7xOjDZ4AQJBYWT_-VOYlxQFCvaj_
視頻編碼順序與視頻的播放順序,並不完全相同
視頻編碼時,如果采用了B幀編碼,由於B幀很多時候都是雙向預測得來的,這時會先編碼B幀的后向預測圖像(P幀),然后再進行B幀編碼,
因此會把視頻原來的播放順序打亂,以新的編碼順序輸出碼流
而在解碼斷接收到碼流后,需要把順序還原成原本的播放順序,以輸出正確的視頻
在編解碼中,視頻的播放順序序號叫做POC(picture order count)
POC有兩種類型:
1、把POC的低位編進碼流內 (pic_order_cnt_type = 0)
2、依賴frame_num求解POC (pic_order_cnt_type = 1)
對於第一種類型,POC的低位pic_order_cnt_lsb可以從碼流內得到,而POC的高位PicOrderCntMsb則要求解碼器自行計數
計數方式依賴於前一編碼幀(PrevPicOrderCntMsb與PrevPicOrderCntLsb),代碼如下
// Calculate the MSBs of current picture if( img->pic_order_cnt_lsb < img->PrevPicOrderCntLsb && ( img->PrevPicOrderCntLsb - img->pic_order_cnt_lsb ) >= ( MaxPicOrderCntLsb / 2 ) ) img->PicOrderCntMsb = img->PrevPicOrderCntMsb + MaxPicOrderCntLsb; else if ( img->pic_order_cnt_lsb > img->PrevPicOrderCntLsb && ( img->pic_order_cnt_lsb - img->PrevPicOrderCntLsb ) > ( MaxPicOrderCntLsb / 2 ) ) img->PicOrderCntMsb = img->PrevPicOrderCntMsb - MaxPicOrderCntLsb; else img->PicOrderCntMsb = img->PrevPicOrderCntMsb;
可以對比下圖分析
如果序列內出現了mmco==5,
如果是場模式,並且出現在底場,PrevPicOrderCntMsb與PrevPicOrderCntLsb都要清零
如果出現在頂場,只需要清零PrevPicOrderCntMsb
如果mmco==5出現在幀模式,也是只清零PrevPicOrderCntMsb
另外如果出現了IDR,那么PrevPicOrderCntMsb與PrevPicOrderCntLsb都要清零
代碼如下
// 1st if(img->idr_flag) { img->PrevPicOrderCntMsb = 0; img->PrevPicOrderCntLsb = 0; } else { if (img->last_has_mmco_5) { if (img->last_pic_bottom_field) { img->PrevPicOrderCntMsb = 0; img->PrevPicOrderCntLsb = 0; } else { img->PrevPicOrderCntMsb = 0; img->PrevPicOrderCntLsb = img->toppoc; } } }
可以對比下圖分析
對於第二種類型,是通過frame_num來計算得出POC
在解析步驟之前通過下圖來分析一下幀序列結構
該序列分3個循環,
除開I幀外,每個循環有相同的結構其中frame_num是由解碼器計數的,這里不在討論范圍內,直接拿來用,
另外有兩個參數是從碼流內(sps)獲取:
num_ref_frames_in_pic_order_cnt_cycle 除I幀外,每個循環內有多少個參考幀(P幀)
offset_for_ref_frame[i] 一個循環內參考幀之間的間隔
假設我們需要計算POC為42的B幀的poc值
有如下步驟:
1、判斷IDR,mmco==5
2、根據frame_num獲取AbsFrameNum,如果是B幀則需要減一
3、計算POC,其中又分為以下幾個步驟:
- 計算一個循環的長度,即offset_for_ref_frame[i]所有元素加起來,4+6+8 = 18
- 計算AbsFrameNum所在循環的位置(9-1)/ 3 = 2 ,即第三個循環
- 計算AbsFrameNum在循環內的偏移(9-1)%3 = 2,偏移2,即需要加上offset_for_ref_frame[0],offset_for_ref_frame[1]兩個
- 計算前兩個循環的長度 2x18 = 36
- 前兩個循環長度加上偏移長度 36 + 4 + 6 = 46
- 由於是B幀,最后要加上offset_for_non_ref_pic,offset_for_non_ref_pic = -2*(連續B幀的數量),而這里由於有多個連續B幀,所以取平均值2,即 offset_for_non_ref_pic = -2*2 = -4
- 最后加上delta_pic_order_cnt[0] = 0,delta_pic_order_cnt[1] = 0 , 即POC = 42
代碼如下:
case 1: // POC MODE 1 // 1st if(img->idr_flag) { img->FrameNumOffset=0; // first pix of IDRGOP, img->delta_pic_order_cnt[0]=0; //ignore first delta if(img->frame_num) error("frame_num != 0 in idr pix", -1020); } else { if (img->last_has_mmco_5) { img->PreviousFrameNumOffset = 0; img->PreviousFrameNum = 0; } if (img->frame_num<img->PreviousFrameNum) { //not first pix of IDRGOP img->FrameNumOffset = img->PreviousFrameNumOffset + img->MaxFrameNum; } else { img->FrameNumOffset = img->PreviousFrameNumOffset; } } // 2nd if(active_sps->num_ref_frames_in_pic_order_cnt_cycle) img->AbsFrameNum = img->FrameNumOffset+img->frame_num; else img->AbsFrameNum=0; if(img->disposable_flag && img->AbsFrameNum>0) img->AbsFrameNum--; // 3rd img->ExpectedDeltaPerPicOrderCntCycle=0; if(active_sps->num_ref_frames_in_pic_order_cnt_cycle) for(i=0;i<(int) active_sps->num_ref_frames_in_pic_order_cnt_cycle;i++) img->ExpectedDeltaPerPicOrderCntCycle += active_sps->offset_for_ref_frame[i]; if(img->AbsFrameNum) { img->PicOrderCntCycleCnt = (img->AbsFrameNum-1)/active_sps->num_ref_frames_in_pic_order_cnt_cycle; img->FrameNumInPicOrderCntCycle = (img->AbsFrameNum-1)%active_sps->num_ref_frames_in_pic_order_cnt_cycle; img->ExpectedPicOrderCnt = img->PicOrderCntCycleCnt*img->ExpectedDeltaPerPicOrderCntCycle; for(i=0;i<=(int)img->FrameNumInPicOrderCntCycle;i++) img->ExpectedPicOrderCnt += active_sps->offset_for_ref_frame[i]; } else img->ExpectedPicOrderCnt=0; if(img->disposable_flag) img->ExpectedPicOrderCnt += active_sps->offset_for_non_ref_pic; if(img->field_pic_flag==0) { //frame pix img->toppoc = img->ExpectedPicOrderCnt + img->delta_pic_order_cnt[0]; img->bottompoc = img->toppoc + active_sps->offset_for_top_to_bottom_field + img->delta_pic_order_cnt[1]; img->ThisPOC = img->framepoc = (img->toppoc < img->bottompoc)? img->toppoc : img->bottompoc; // POC200301 } else if (img->bottom_field_flag==0) { //top field img->ThisPOC = img->toppoc = img->ExpectedPicOrderCnt + img->delta_pic_order_cnt[0]; } else { //bottom field img->ThisPOC = img->bottompoc = img->ExpectedPicOrderCnt + active_sps->offset_for_top_to_bottom_field + img->delta_pic_order_cnt[0]; } img->framepoc=img->ThisPOC; img->PreviousFrameNum=img->frame_num; img->PreviousFrameNumOffset=img->FrameNumOffset; break;
計算POC還有一種類型,這種最簡單,直接通過frame_num推導,應用在沒有連續的非參考幀的情況下(即一個間隔最多只能包含一個非參考幀)。
即沒有B幀的,這種最簡單,直接通過frame_num推導,
但是應該注意,在這種情況下不存在連續 的非參考圖象(注釋),且解碼輸出的順序和顯示輸出順序一致(注釋),意思就是說不出現B幀,但可以出現非參考的P場,這也是為什么當 nal_ref_idc=0的時候
tempPicOrderCnt = 2 * ( FrameNumOffset + frame_num ) –1的情況。這里保證了參考場的POC始終為偶數,並且大於同幀的另外一個場
代碼如下
case 2: // POC MODE 2 if(img->idr_flag) // IDR picture { img->FrameNumOffset=0; // first pix of IDRGOP, img->ThisPOC = img->framepoc = img->toppoc = img->bottompoc = 0; if(img->frame_num) error("frame_num != 0 in idr pix", -1020); } else { if (img->last_has_mmco_5) { img->PreviousFrameNum = 0; img->PreviousFrameNumOffset = 0; } if (img->frame_num<img->PreviousFrameNum) img->FrameNumOffset = img->PreviousFrameNumOffset + img->MaxFrameNum; else img->FrameNumOffset = img->PreviousFrameNumOffset; img->AbsFrameNum = img->FrameNumOffset+img->frame_num; if(img->disposable_flag) img->ThisPOC = (2*img->AbsFrameNum - 1); else img->ThisPOC = (2*img->AbsFrameNum); if (img->field_pic_flag==0) img->toppoc = img->bottompoc = img->framepoc = img->ThisPOC; else if (img->bottom_field_flag==0) img->toppoc = img->framepoc = img->ThisPOC; else img->bottompoc = img->framepoc = img->ThisPOC; } if (!img->disposable_flag) img->PreviousFrameNum=img->frame_num; img->PreviousFrameNumOffset=img->FrameNumOffset; break;