xvid有兩種編碼方式:single pass和twopass
single pass模式編碼簡(jiǎn)單,速度也快,但最終效果不如twopass。
twopass就是視頻壓制需要經(jīng)過(guò)兩次編碼,分別為twopass-1st pass(簡(jiǎn)稱1pass)和twopass-2nd pass(簡(jiǎn)稱2pass)
1pass時(shí),編碼器會(huì)用最高質(zhì)量編碼采集可供第2次運(yùn)算參考的畫面信息,而在2 pass時(shí)。編碼器會(huì)根據(jù)第一次壓縮獲得的信息和用戶指定的文件大小,自動(dòng)分配比特率,使需要高流量的運(yùn)動(dòng)畫面分配到更多的空間,更高的比特率來(lái)保證畫面質(zhì)量。相對(duì)的,對(duì)于那些不包含太多運(yùn)動(dòng)信息的靜態(tài)畫面則用較低的比特率。追求畫質(zhì)的朋友當(dāng)然會(huì)選擇這種方式,但運(yùn)算比single pass更費(fèi)時(shí)。
接下來(lái)介紹一些基本概念:
Q值——量化值,它被用來(lái)描述1幀的質(zhì)量,每幀都有一個(gè)Q值,取值范圍在1-31之間。Q值越小,畫質(zhì)越好,比特率越大
I-frame——關(guān)鍵幀,常被縮寫為IF。關(guān)鍵幀是構(gòu)成一個(gè)幀組的第一幀。IF保留了一個(gè)場(chǎng)景的所有信息
P-frame——未來(lái)單項(xiàng)預(yù)測(cè)幀,縮寫為PF,只儲(chǔ)存與之前一個(gè)已解壓畫面的差值
B-frame——雙向預(yù)測(cè)幀,縮寫為BF,除了參考之前解壓的畫面以外,也會(huì)參考后一幀的畫面信息
編碼流程:
各變量的設(shè)置:創(chuàng)建xvid_enc_frame_t和xvid_enc_stats_t,分別用于傳入?yún)?shù)和統(tǒng)計(jì)編碼結(jié)果。
具體過(guò)程:
設(shè)置傳入圖像數(shù)據(jù)和圖像色彩空間
設(shè)置傳出的碼流
設(shè)置vol的標(biāo)志
設(shè)置幀的編碼類型
設(shè)置量化因子
設(shè)置運(yùn)動(dòng)估計(jì)算法集合
設(shè)置vop的標(biāo)志
編碼器提供的函數(shù)
1, xvid_global(NULL, XVID_GBL_INIT, &xvid_gbl_init, NULL);
含義:根據(jù)cpu的特性使用相應(yīng)匯編優(yōu)化的函數(shù)
2, xvid_encore(NULL, XVID_ENC_CREATE, &xvid_enc_create, NULL);
含義:初始化編碼器。
具體過(guò)程:
創(chuàng)建編碼器句柄,并根據(jù)傳入的參數(shù)設(shè)置各變量的值,并且分配要使用的內(nèi)存,用于存放重建幀,參考幀(1/2像素精度)。以及各種臨時(shí)變量。并且做好碼率控制的初始化。
3, xvid_encore(enc_handle, XVID_ENC_ENCODE, &xvid_enc_frame, &xvid_enc_stats);
目的:編碼一幀
具體過(guò)程:
{
初始化寫碼流。
如果有必要,轉(zhuǎn)換色彩空間,并且把原始圖像拷貝到有邊框的圖像空間,但是沒(méi)有擴(kuò)展邊框。
將重建幀交換成參考幀
從幀隊(duì)列中獲取當(dāng)前幀
設(shè)置Encoder結(jié)構(gòu)體的current結(jié)構(gòu)體的vol_flags,vop_flags,motion_flags,fcode,bcode和quant字段。
調(diào)用call_plugins,在里面調(diào)用rc_single_before做碼率控制的初始化,以及對(duì)current結(jié)構(gòu)體的其他變量進(jìn)一步設(shè)置
通過(guò)幀號(hào)或者M(jìn)Eanalysis函數(shù)分析來(lái)確定編碼類型,并且根據(jù)用戶的設(shè)置作修正。
MEanalysis的原理是,如果某個(gè)宏塊的殘差的sad大于該宏塊的平均值的偏離,那么使用intra方式,否則使用inter方式,然后對(duì)這些宏塊進(jìn)行統(tǒng)計(jì),得到整幀的編碼方式。
如果編碼類型是I_VOP
{
設(shè)置Encoder->mbParam->vol_flags
設(shè)置Encoder->mbParam.par
根據(jù)vol_flags設(shè)置vop_flags
調(diào)用FrameCodeI以I幀的方式編碼
調(diào)用call_plugins,在里面調(diào)用rc_single_after,進(jìn)行碼率控制。
}
如果編碼類型是P_VOP
{
用mbParam.vol_flags固定住pEnc->current->vol_flags
調(diào)用FrameCodeP以P幀的方式編碼
調(diào)用call_plugins,在里面調(diào)用rc_single_after,進(jìn)行碼率控制。
}
}// xvid_encore
4, static int FrameCodeI(Encoder * pEnc, Bitstream * bs)
目的:將一幀圖像編碼成一個(gè)I幀
具體過(guò)程:
以XVID_PLG_FRAME參數(shù)調(diào)用call_plugins,該函數(shù)目前的作用是設(shè)置dquant,可以在該函數(shù)中設(shè)置最好質(zhì)量。
調(diào)用SetMacroblockQuants,為每個(gè)宏塊設(shè)置量化因子,所以也可以在這里設(shè)置最好質(zhì)量
調(diào)用BitstreamWriteVolHeader,寫vol
調(diào)用set_timecodes,設(shè)置時(shí)間編碼。
調(diào)用BitstreamPad,填充bit至字節(jié)對(duì)齊
調(diào)用BitstreamWriteVopHeader,填寫vop頭
依次讀取每一個(gè)宏塊,進(jìn)行編碼
{
調(diào)用CodeIntraMB設(shè)置編碼模式為intra,將所有和運(yùn)動(dòng)有關(guān)的變量設(shè)為0
調(diào)用MBTransQuantIntra進(jìn)行變換編碼
{
調(diào)用MBTrans8to16將像素的表示方法從8bit擴(kuò)大到16bit
調(diào)用MBfDCT對(duì)像素進(jìn)行變換編碼
調(diào)用MBQuantIntra對(duì)dct系數(shù)進(jìn)行intra方式的量化
調(diào)用MBDeQuantIntra對(duì)dct系數(shù)進(jìn)行intra方式的反量化
調(diào)用MBiDCT將恢復(fù)的dct系數(shù)進(jìn)行反變換
調(diào)用MBTrans16to8將恢復(fù)的16bit像素飽和到8bit,組成重建宏塊
}//MBTransQuantIntra
調(diào)用MBPrediction作acdc預(yù)測(cè)
{
調(diào)用get_dc_scaler函數(shù)得到量化系數(shù)
調(diào)用predict_acdc得到預(yù)測(cè)方向以及在該預(yù)測(cè)方向上的和當(dāng)前塊的同一量化水平的預(yù)測(cè)值
調(diào)用calc_acdc_bits以確定是只使用DC預(yù)測(cè),還是DCAC預(yù)測(cè)。原理是分別作DC預(yù)測(cè)和DCAC預(yù)測(cè),分別計(jì)算在這2種情況下需要的碼流長(zhǎng)度,以確定哪種方式更節(jié)約碼流。
調(diào)用CodeCoeffIntra_CalcBits,用于確定各種方式下的碼流長(zhǎng)度
根據(jù)預(yù)測(cè)模式的不同,恢復(fù)成相應(yīng)的系數(shù)
最后計(jì)算該宏塊的cbp
}//MBPrediction
調(diào)用MBCoding將宏塊編制成碼流
{
調(diào)用CodeBlockIntra將intra宏塊編制成碼流
{
編碼mcbpc
編碼ac預(yù)測(cè)標(biāo)記
編碼cbpy
對(duì)于6個(gè)塊里的每個(gè)塊
首先編碼DC系數(shù)
調(diào)用CodeCoeffIntra對(duì)剩下的63個(gè)系數(shù)進(jìn)行編碼
}//CodeBlockIntra
}//MBCoding
}//依次讀取每一個(gè)宏塊,進(jìn)行編碼
填充bit,直到字節(jié)對(duì)齊
5, static int FrameCodeP(Encoder * pEnc, Bitstream * bs)
含義:將一幀圖片編碼成P幀
具體過(guò)程:
{
如果參考幀還沒(méi)有設(shè)置邊框,那么就調(diào)用image_setedges設(shè)置邊框
如果需要半像素運(yùn)動(dòng)估計(jì),那么就調(diào)用image_interpolate進(jìn)行插值
將一幀填充邊框后的參考幀,分成8*8的小塊,對(duì)于每個(gè)小塊進(jìn)行插值,如下:
調(diào)用interpolate8x8_halfpel_h進(jìn)行水平插值
調(diào)用interpolate8x8_halfpel_v進(jìn)行垂直插值
調(diào)用interpolate8x8_halfpel_hv進(jìn)行對(duì)角線插值
用參數(shù)XVID_PLG_FRAME調(diào)用call_plugins,該函數(shù)目前的作用是設(shè)置dquant,可以在該函數(shù)中設(shè)置最好質(zhì)量。
調(diào)用SetMacroblockQuants,為每個(gè)宏塊設(shè)置量化因子,所以也可以在這里設(shè)置最好質(zhì)量
調(diào)用MotionEstimation做運(yùn)動(dòng)估計(jì)
{
使用MotionFlags變量保存要使用的運(yùn)動(dòng)算法集合
使用skip_thresh保存要達(dá)到skip模式的閥值
使用Data保存運(yùn)動(dòng)估計(jì)要用到的相應(yīng)變量
對(duì)于每個(gè)宏塊,依次執(zhí)行如下操作
{
調(diào)用sad16v計(jì)算本宏塊與參考幀對(duì)應(yīng)位置宏塊的亮度的殘差,將其保存在pMB->sad16中,并按照4個(gè)塊的方式分別存放pMB->sad8[0-3]中
用sad00記錄最大亮度塊殘差的4倍
如果還需要考慮色差塊的因素
調(diào)用sad8兩次,分別計(jì)算u分量和v分量的殘差,都加入pMB->sad16中,并且也加入sad00中
如果該宏塊的量化差值為0,并且sad00又沒(méi)有超過(guò)skip模式的閥值
如果已經(jīng)考慮了色差因素,或者使用xvid_me_SkipDecisionP確認(rèn)符合skip模式。
調(diào)用ZeroMacroblockP將其編碼為skip模式,并置標(biāo)記pMB->mode = MODE_NOT_CODED
根據(jù)采用的運(yùn)動(dòng)估計(jì)算法不同,做相應(yīng)的設(shè)置
調(diào)用SearchP做該宏塊的運(yùn)動(dòng)估計(jì)
{
確定是否使用inter4v模式,并記錄之
調(diào)用get_range確定運(yùn)動(dòng)搜索的范圍,并記錄在Data中
調(diào)用get_pmvdata2,以獲得左,上,右上的運(yùn)動(dòng)向量,以及它們對(duì)應(yīng)的sad,存入pmv[1-3]和Data->temp[1-3]。然后計(jì)算它們的中值,并且存放于pmv[0],并且把最小的sad存放于Data->temp[0]
設(shè)置Data的當(dāng)前宏塊的yuv字段。設(shè)置Data->RefP[0-5]為參考幀的同一宏塊的整像素y,水平半象素y,垂直半象素y,對(duì)角線y,u,v。
設(shè)置Data->lambda16和Data->lambda8,其含義可能是運(yùn)動(dòng)向量對(duì)帶寬的占用折合到sad的值
設(shè)置qpel和方向
如果采用qpel,調(diào)用get_qpmv2計(jì)算用qple方式下的估計(jì)中值,存入ata->predMV;否則,Data->predMV為0。
調(diào)用d_mv_bits計(jì)算mv需要的編碼bit,用于修正pMB->sad16和pMB->sad8[0],并將Data->iMinSAD[0-4]設(shè)置為pMB->sad16和pMB->sad8[0-3],也就是0向量對(duì)應(yīng)的各SAD。
如果不采用率失真決策模型,并且不是當(dāng)前幀的第一宏塊,那么使用一種方法設(shè)置閥值threshA,否則閥值threshA為512。
調(diào)用PreparePredictionsP,對(duì)pmv作進(jìn)一步的設(shè)置,做運(yùn)算前的準(zhǔn)備。
{
設(shè)置pmv[0]為0向量
設(shè)置pmv[1]為中值向量的偶數(shù)值
設(shè)置pmv[2]為參考幀相同位置宏塊的第0塊運(yùn)動(dòng)向量的偶數(shù)值
如果該宏塊有左邊宏塊,設(shè)置pmv[3]為左邊宏塊的第1塊的運(yùn)動(dòng)向量的偶數(shù)值,否則為0
如果該宏塊有上面宏塊,設(shè)置pmv[4]為上面宏塊的第2塊的運(yùn)動(dòng)向量的偶數(shù)值,否則為0
如果該宏塊有右上宏塊,設(shè)置pmv[5]為右上宏塊的第2塊的運(yùn)動(dòng)向量的偶數(shù)值,否則為0
如果該宏塊有右下宏塊,設(shè)置pmv[6]為參考幀的相同宏塊的右下宏塊的第0塊的運(yùn)動(dòng)向量的偶數(shù)值,否則為0。
}//PreparePredictionsP
如果使用inter4v,設(shè)置CheckCandidate為CheckCandidate16,否則設(shè)置為CheckCandidate16no4v
逐一檢查mpv[1-6]這六個(gè)最可能運(yùn)動(dòng)向量,如果發(fā)現(xiàn)他們與以前的運(yùn)動(dòng)不同,就調(diào)用CheckCandidate做運(yùn)動(dòng)估計(jì),過(guò)程如下:
{
檢查要做運(yùn)動(dòng)估計(jì)的運(yùn)動(dòng)向量是否越界
通過(guò)該運(yùn)動(dòng)向量獲得所指向數(shù)據(jù)塊的指針
調(diào)用sad16v,記錄下4個(gè)8*8塊的SAD值,存入data->temp[0-3]中,并將他們的和存入臨時(shí)變量sad中。
對(duì)sad和data->temp[0]做基于運(yùn)動(dòng)向量的修正。
如果要考慮色差因素,調(diào)用xvid_me_ChromaSAD計(jì)算額外的SAD,累加至sad中。
如果sad小于data->iMinSAD[0],那么設(shè)置data->iMinSAD[0],data->currentMV[0],和data->dir。注意,此時(shí)的data->dir記錄的不是鉆石搜索的方向,而是當(dāng)前向量是pmv數(shù)組的第幾個(gè)元素。
逐一檢查data->temp[0-3],如果他們小于data->iMinSAD[1-4],那么修改data->iMinSAD[1-4]和data->currentMV[1-4]
}//CheckCandidate
如果當(dāng)前最優(yōu)運(yùn)動(dòng)向量,即Data->iMinSAD[0],小于threshA?或者當(dāng)前最優(yōu)運(yùn)動(dòng)向量等于參考幀相同位置宏塊的運(yùn)動(dòng)向量,并且對(duì)應(yīng)的SAD值又比他的小?
就不再做inter4v的搜索
否則,就做inter4v的搜索
{
使用make_mask逐一檢查存放于pmv的所有運(yùn)動(dòng)向量,察看是否位于欲搜索的鉆石形的頂點(diǎn)。如果是,則在mask變量中標(biāo)記之。
根據(jù)MotionFlags確定使用的搜索函數(shù),根據(jù)當(dāng)前設(shè)置,MainSearchPtr = xvid_me_AdvDiamondSearch
調(diào)用xvid_me_AdvDiamondSearch進(jìn)行搜索,過(guò)程如下:
{
bDirection既表明了上次嘗試的方向,又表明本次可以嘗試的方向
x,y為鉆石搜索的位置的中心點(diǎn)坐標(biāo)
for(;;)
{
如果可以嘗試左邊,那么調(diào)用CheckCandidate嘗試左邊
如果可以嘗試右邊,那么調(diào)用CheckCandidate嘗試右邊
如果可以嘗試上邊,那么調(diào)用CheckCandidate嘗試上邊
如果可以嘗試下邊,那么調(diào)用CheckCandidate嘗試下邊
如果有更好的方向
{
bDirection = 更好的方向
如果更好的方向是左右方向,那么測(cè)試該位置的上下方向
否則,那么測(cè)試該位置的左右方向
如果這次又找到了更好的方向
將更好的方向累加到bDirection
將更好的位置存入x,y
}
否則
{
根據(jù)去搜索臨近未搜索的點(diǎn),具體規(guī)則如下:
如果bDirection = = 2,表明搜索方向是趨向右邊的,那么搜索當(dāng)前中心點(diǎn)的右上點(diǎn)和右下點(diǎn)。
如果bDirection = = 1,表明搜索方向是趨向左邊的,那么搜索當(dāng)前中心點(diǎn)的左上點(diǎn)和左下點(diǎn)。
如果bDirection = = 2+4,表明搜索方向是趨向右上的,那么再搜索當(dāng)前中心點(diǎn)的左上點(diǎn),右上點(diǎn)和右下點(diǎn)。
如果bDirection = = 4,表明搜索方向是趨向上邊的,那么搜索當(dāng)前中心點(diǎn)的左上點(diǎn)和右上點(diǎn)。
如果bDirection = = 8,表明搜索方向是趨向下邊的,那么搜索當(dāng)前中心點(diǎn)的左下點(diǎn)和右下點(diǎn)。
如果bDirection = = 1+4,表明搜索方向是趨向左上的,那么再搜索當(dāng)前中心點(diǎn)的左下點(diǎn),左上點(diǎn)和右上點(diǎn)。
如果bDirection = = 2+8,表明搜索方向是趨向右下的,那么再搜索當(dāng)前中心點(diǎn)的左下點(diǎn),左上點(diǎn)和右上點(diǎn)。
如果bDirection = = 1+8,表明搜索方向是趨向左下的,那么再搜索當(dāng)前中心點(diǎn)的左上點(diǎn),左下點(diǎn)和右下點(diǎn)。
否則的話,則認(rèn)為本輪搜索沒(méi)有找到更好的點(diǎn),那么再搜索當(dāng)前中心點(diǎn)的左上點(diǎn),左下點(diǎn),右上點(diǎn),右下點(diǎn)。
}
如果沒(méi)有找到更好的方向,從函數(shù)中返回
更新bDirection為更好的方向
更新x,y為更好的位置
}//for(;;)
}//xvid_me_AdvDiamondSearch
如果運(yùn)動(dòng)估計(jì)算法使用了XVID_ME_EXTSEARCH16,那么
{
設(shè)置startMV = Data->predMV
設(shè)置backupMV為當(dāng)前最佳運(yùn)動(dòng)向量
如果startMV和backupMV不相等
{
調(diào)用CheckCandidate計(jì)算位置為startMV的SAD
調(diào)用xvid_me_DiamondSearch做以startMV為起點(diǎn)的搜索,過(guò)程如下:
{
for(;;)
{
如果可以嘗試左邊,那么調(diào)用CheckCandidate嘗試左邊
如果可以嘗試右邊,那么調(diào)用CheckCandidate嘗試右邊
如果可以嘗試上邊,那么調(diào)用CheckCandidate嘗試上邊
如果可以嘗試下邊,那么調(diào)用CheckCandidate嘗試下邊
如果沒(méi)有更好的方向,退出
bDirection = 更好的方向
x,y = 更好的位置
如果更好的方向是左右方向,那么測(cè)試該位置的上下方向
否則,那么測(cè)試該位置的左右方向
如果這次又找到了更好的方向
{
bDirection += 更好的方向
x,y = 更好的位置
}
}
}//xvid_me_DiamondSearch
將這次搜索結(jié)果和上次搜索結(jié)果比較,記錄最佳的SAD和位置。
}//如果startMV和backupMV不相等
設(shè)置startMV = {1,1}
設(shè)置backupMV為當(dāng)前最佳運(yùn)動(dòng)向量
如果startMV和backupMV不相等
{
調(diào)用CheckCandidate計(jì)算位置為startMV的SAD
調(diào)用xvid_me_DiamondSearch做以startMV為起點(diǎn)的搜索,過(guò)程如下:
將這次搜索結(jié)果和上次搜索結(jié)果比較,記錄最佳的SAD和位置。
}
}//如果運(yùn)動(dòng)估計(jì)算法使用了XVID_ME_EXTSEARCH16
}//否則,就做inter4v的搜索
如果沒(méi)有采用1/4像素運(yùn)動(dòng)估計(jì)算法
{
如果采用了XVID_ME_HALFPELREFINE16算法
調(diào)用xvid_me_SubpelRefine
按順時(shí)針?lè)较?次調(diào)用CheckCandidate16,得到最好的1/2像素位置
}
否則
略
如果當(dāng)前SAD足夠小,那么inter4v = 0
如果采用inter4v
{
4次調(diào)用Search8來(lái)搜索4個(gè)8*8塊的最佳運(yùn)動(dòng)向量,每一次搜索的規(guī)則如下:
{
如果采用1/4像素運(yùn)動(dòng)估計(jì),略。否則
調(diào)用get_pmv2取得本塊的中值
計(jì)算第一塊以外快的d_mv_bits
用Data->lambda8修正該塊當(dāng)前的SAD,但是第0塊是不用修正的。
如果使用了XVID_ME_EXTSEARCH8 | XVID_ME_HALFPELREFINE8 | XVID_ME_QUARTERPELREFINE8,那么
{
Data->RefP[0-3] = 參考幀的整像素,水平半象素,垂直半象素,對(duì)角線半象素的對(duì)應(yīng)宏塊的對(duì)應(yīng)塊的起始地址。
Data->Cur = 當(dāng)前幀的當(dāng)前宏塊的當(dāng)前塊的起始地址
利用get_range得到運(yùn)動(dòng)搜索的范圍
根據(jù)MotionFlags的指示,設(shè)定運(yùn)動(dòng)估計(jì)MainSearchPtr的算法,當(dāng)前設(shè)置為MainSearchPtr = xvid_me_AdvDiamondSearch。
調(diào)用xvid_me_AdvDiamondSearch做運(yùn)動(dòng)估計(jì),其中做SAD的函數(shù)是CheckCandidate8,該函數(shù)類似于CheckCandidate16
如果不采用1/4像素運(yùn)動(dòng)估計(jì),并且又采用了XVID_ME_HALFPELREFINE8,那么調(diào)用xvid_me_SubpelRefine
按順時(shí)針?lè)较?次調(diào)用CheckCandidate8,得到最好的1/2像素位置
如果采用了1/4像素運(yùn)動(dòng)估計(jì),略
}// XVID_ME_EXTSEARCH8 | XVID_ME_HALFPELREFINE8 | XVID_ME_QUARTERPELREFINE8
如果采用1/4運(yùn)動(dòng)估計(jì)
略
否則
記錄pMB->pmvs[block] = 當(dāng)前找到的最佳位置與預(yù)測(cè)位置的差值
將這次的搜索存入相應(yīng)OldData的字段,以及pMB的相應(yīng)字段
}// Search8
如果考慮色差的因素,并且又不考慮率失真算法
{
根據(jù)是否采用1/4像素運(yùn)動(dòng)估計(jì)算出色差的運(yùn)動(dòng)向量
計(jì)算u,v的SAD,將其作為Data->iMinSAD[1]的修正
}
} //如果采用inter4v
否則,Data->iMinSAD[1]為足夠大的值
}//SearchP
調(diào)用ModeDecision_SAD確定該宏塊的類型
判斷該宏塊要采取的編碼方式,MODE_INTER,MODE_INTER4V,MODE_NOT_CODED,MODE_INTRA
調(diào)用motionStatsPVOP做一些統(tǒng)計(jì)工作
具體過(guò)程略
}//對(duì)于每個(gè)宏塊,依次執(zhí)行如下操作
做一些最后的設(shè)置
}//MotionEstimation
調(diào)用set_timecodes設(shè)置時(shí)間戳
調(diào)用BitstreamWriteVopHeader寫VOP頭
具體過(guò)程略
對(duì)于每一個(gè)宏塊,依次執(zhí)行如下操作
{
如果該宏塊的編碼模式是MODE_INTRA或者M(jìn)ODE_INTRA_Q
{
調(diào)用CodeIntraMB設(shè)置編碼模式為intra,將所有和運(yùn)動(dòng)有關(guān)的變量設(shè)為0
調(diào)用MBTransQuantIntra進(jìn)行變換編碼
調(diào)用MBCoding將該宏塊編制成碼流
Continue
}
調(diào)用MBMotionCompensation做運(yùn)動(dòng)補(bǔ)償
{
如果編碼模式是MODE_NOT_CODED
用參考幀的相應(yīng)宏塊替代當(dāng)前幀的當(dāng)前宏塊
Return
如果編碼模式是MODE_NOT_CODED或者M(jìn)ODE_INTER或者M(jìn)ODE_INTER_Q
{
如果mb->mcsel不為0
做GMC的處理
Return
計(jì)算運(yùn)動(dòng)向量dx,dy
調(diào)用compensate16x16_interpolate進(jìn)行運(yùn)動(dòng)補(bǔ)償
{
如果采用1/4像素運(yùn)動(dòng)估計(jì)
略
否則,調(diào)用get_ref計(jì)算用于運(yùn)動(dòng)補(bǔ)償?shù)膮⒖己陦K的指針
調(diào)用4次transfer_8to16sub做亮度塊的運(yùn)動(dòng)補(bǔ)償,使得臨時(shí)數(shù)組里存放的是殘差,而原始圖像里存放的是參考快的數(shù)據(jù)。
}//compensate16x16_interpolate
計(jì)算出用于色差運(yùn)動(dòng)補(bǔ)償?shù)膁x,dy
}//MODE_NOT_CODED或者M(jìn)ODE_INTER或者M(jìn)ODE_INTER_Q
否則,那就是MODE_INTER4V
{
根據(jù)是否使用1/4像素運(yùn)動(dòng)估計(jì),計(jì)算出4個(gè)色度塊的運(yùn)動(dòng)向量
以這4個(gè)運(yùn)動(dòng)向量為參數(shù),調(diào)用4次compensate8x8_interpolate ,該操作類似于compensate16x16_interpolate,不同在于一次只計(jì)算一個(gè)塊。
計(jì)算出用于色差運(yùn)動(dòng)補(bǔ)償?shù)膁x,dy
}
調(diào)用CompensateChroma計(jì)算色差塊的運(yùn)動(dòng)補(bǔ)償
{
調(diào)用interpolate8x8_switch2計(jì)算出u的插值
調(diào)用interpolate8x8_halfpel_v或者interpolate8x8_halfpel_h或者interpolate8x8_halfpel_hv做實(shí)際的插值操作,或者直接返回
調(diào)用transfer_8to16sub_c做u份量的運(yùn)動(dòng)補(bǔ)償
調(diào)用interpolate8x8_switch2計(jì)算出v的插值
調(diào)用interpolate8x8_halfpel_v或者interpolate8x8_halfpel_h或者interpolate8x8_halfpel_hv做實(shí)際的插值操作,或者直接返回
調(diào)用transfer_8to16sub_c做v份量的運(yùn)動(dòng)補(bǔ)償
}//CompensateChroma
}//MBMotionCompensation
如果需要編碼,那么用MBTransQuantInter進(jìn)行編碼,并把結(jié)果返回給pMB->cbp
{
調(diào)用MBfDCT進(jìn)行宏塊變換編碼
調(diào)用6次fdct
調(diào)用MBQuantInter進(jìn)行量化
{
對(duì)于宏塊里的每一塊
{
調(diào)用quant_h263_inter進(jìn)行量化
如果在量化后,前三個(gè)系數(shù)為0,并且系數(shù)的絕對(duì)值之和小于閥值,那么標(biāo)記該塊為全0塊,將標(biāo)記存入cbp。否則,標(biāo)記為非全0塊,也將標(biāo)記存入cbp
}
}//MBQuantInter
調(diào)用MBDeQuantInter反量化
{
確定要使用的反量化函數(shù)
對(duì)于六個(gè)塊里的每個(gè)塊,如果cbp表示許可,都調(diào)用dequant_h263_inter反量化
}//MBDeQuantInter
調(diào)用MBiDCT做反離散余弦變換
對(duì)于六個(gè)塊里的每個(gè)塊,如果cbp表示許可,都調(diào)用idct_int32反量化
調(diào)用MBTrans16to8將恢復(fù)出的殘差構(gòu)成重建圖像
{
確定具體執(zhí)行的函數(shù),分為transfer_16to8copy和transfer_16to8add
找到該宏塊的y,u,v分量起始地址
對(duì)于六個(gè)塊里的每個(gè)塊,如果cbp表示許可,調(diào)用相應(yīng)得函數(shù)執(zhí)行重建。
}// MBTrans16to8
}//MBTransQuantInter
如果無(wú)殘差,并且編碼方式為MODE_INTER,并且?guī)绞绞荘幀,并且向量2分量都為0,那么可以考慮skip模式
如果可以考慮skip模式,則做進(jìn)一步檢驗(yàn),如果檢驗(yàn)通過(guò),那么
{
編碼模式為MODE_NOT_CODED,并且在碼流里做標(biāo)記
Continue
}
調(diào)用MBCoding將這個(gè)宏塊寫入碼流
{
寫入非NOT_CODED標(biāo)記
調(diào)用CodeBlockInter寫入碼流
{
編碼mcbpc
編碼cbpy
調(diào)用CodeVector編碼運(yùn)動(dòng)向量
對(duì)六個(gè)塊,如果cbp只是需要編碼,調(diào)用CodeCoeffInter進(jìn)行編碼
}//CodeBlockInter
}// MBCoding
}//對(duì)于每一個(gè)宏塊,依次執(zhí)行如下操作
更新fcode
為下一幀的編碼做簡(jiǎn)單的更新設(shè)置
統(tǒng)計(jì)該幀編碼長(zhǎng)度
}// FrameCodeP