我對HM代碼結構理解的啟蒙文章,轉自實驗室前輩朱師兄的博客:http://blog.csdn.net/spark19851210/article/details/8964559
1. 環境配置
這個文檔描述的版本是HM6.0
運行的方法如下可參考之前的文章:
2. 編碼端主函數的調用
主函數中會調用create函數,但是這里面是空函數,所以不做任何操作
encode是非常重要的函數,負責了實際的編碼工作,在里面調用m_cTEncTop的encode
函數對每個GOP進行編碼,並對每個GOP調用compressGOP。GOP的概念在HEVC中規定的並沒有H.264/AVC那么嚴格,在H.264/AVC中,GOP是以I slice開始,而HEVC中並沒有這樣的規定,相當於弱化了HEVC中GOP的概念。
3. GOP划分為Slice
GOP進而會划分為slice,有raster順序的划分和tile的划分方式,對每個slice會調用
compressSlice來的對其選出最優的參數。然后調用encodeSlice來對其進行實際的熵編碼工作。
4. Slice的划分(Slice到LCU)
到Slice層面后,會划分等大的LCU,對每個CU進行compress和encode的工作,調用的函數分別為compressCU和encodeCU。而在CompressSlice和EncoderSlice中都會調用encodeCU,是為了保證后續計算碼率的准確性,重點在於保證了cabac的狀態是准確的。下面將介紹compressCU和encodeCU,由於RDO的時候會編碼CU的信息,所以着重介紹compressCU
5. compressCU
把一個slice內部的圖像划分為K個LCU,同時這K個LCU是以raster的順序來進行掃描的。LCU的尺寸默認為64x64,可以通過配置文件中的MaxCUWidth,MaxCUHeight來進行設置。而每個LCU會調用如下的compressCU函數去決定編碼的參數。 而LCU是采用四叉樹的表示結構,每個LCU會被遞歸的划分為4個子CU,並根據RD代價來確定是否進行划分,而每個LCU相當於樹的第1層,所以他會調用xCompressCU。且在xCompressCU中會對每個子CU遞歸的調用xCompressCU,如下圖所示,當然子CU的尺寸是當前CU的四分之一。
然而在每次xCompressCU時,會對當前CU進行intra模式的測試,如果是B或Pslice,還對其進行merge和inter模式的測試。下面分別介紹intra,inter和merge相關的代碼
5.1 幀內
intra的調用流程如下:
estIntraPredQT主要做模式選擇的工作,負責選出對於當前PU的最優模式,如DC,或方向性,或planar。estIntraPredChromaQT做了類似的工作,不過是針對於色度。xRecurIntraCodingQT和xRecurIntraChromaCodingQT函數是依據給定的候選模式進行PU的分割,進而依據TU進行重建estIntraPredQT。
5.1.1 estIntraPredQT
在這里面首先對N個候選模式進行粗粒度篩選
代價函數為,從M個模式選出N個最可能的候選模式。所涉及的函數:
l predIntraLumaAng: 算出當前PU的預測值
l calcHAD: 計算SATD代價
l xModeBitsIntra: 計算當前模式所耗費的比特數目
l xUpdateCandList: 更新模式的代價,保持前N個模式的代價最小
在選出N個模式后,這N個模式會進入xRecurIntraCodingQT函數從而進行TU的分割
5.1.2 xRecurIntraCodingQT
為了加速RQT過程,這個函數會被調用兩次:
第一次的調用不進行PU分割為TU的過程,PU直接轉換為TU,只為算出N個模式的RD代價,從而選出一個最優的,在這個最優的模式被選出后,會第二次調用這個函數,再對這個最優的模式進行PU的分割。區分第一次和第二次調用的變量是bCheckFirst。這個過程所涉及的函數有:
l xIntraCodingLumaBlk: 進行對當前TU進行求殘差,對殘差變換,量化,反量化,反變換,重建當前TU等一系列編碼工作,並求得失真
l xGetIntraBitsQT: 求出當前模式的所有信息進行熵編碼會產生的比特數
l calcRdCos:根據xIntraCodingLumaBlk得到的失真和xGetIntraBitsQT產生的比特數目進行RD代價的計算,從而比較各模式的優劣
l xSetIntraResultQT:保存最優模式的數據
5.1.3 estIntraPredChromaQT
estIntraPredChromaQT會決定當前PU采用哪個色度模型對色度分量進行編碼,其中涉及的函數如下:
l getAllowedChromaDir:獲得可用的色度
l xRecurIntraChromaCodingQT:進行當前PU的色度分量的一系列編碼工作,並求得失真
l xGetIntraBitsQT:進行當前PU的色度分量的熵編碼工作,並得到產生的比特數
l calcRdCost:根據失真和碼率進行率失真代價的計算
l xSetIntraResultChromaQT:保存當前色度最優模式的信息
5.1.4 xRecurIntraChromaCodingQT
這個函數是求PU的色度分量的殘差,首先會對PU進行分割,分割的層數與亮度完全一致。涉及的函數如下:
l xIntraCodingChromaBlk:對當前TU進行色度信息的編碼工作,如求殘差,變換,量化,反量化,反變換,重建等一系列工作
5.2 幀間
幀間按默認的配置文件設置有兩種:inter模式和merge模式
5.2.1 inter模式和merge模式的流程
主要調用流程:
Inter流程
Merge流程
流程中涉及的函數:
l predInterSearch進行的是ME和MC的過程,當然會測試各種情況
l Motioncompensation進行的是MC的工作,由於merge模式沒有ME的過程,是將已有的MV信息直接代替當前PU的MV,所以直接進行MC
l encodeResAndCalcRdInterCU是對得到預測值后求出的殘差進行TU的划分及RD代價的計算
5.2.2 encodeResAndCalcRdInterCU
涉及的主要函數:
l encodeSkipFlag:編碼SKIP模式的flag
l encodeMergeIndex:編碼選用哪套運動參數的索引
xEstimateResidualQT:在非SKIP模式的時候要進行RQT的決定,即PU分割為什么樣的TU在這個函數里面確定
l xAddSymbolBitsInter:計算當前CU的信息在進行熵編碼時所產生的比特數
l xSetResidualQTData:保存當前CU的最優的殘差信息
6. 一些其他常用的函數說明:
6.1 預測
6.1.1 幀內
l initPattern:判斷周圍塊的存在性
l initAdiPattern:獲取周圍像素的值當做生成預測值的像素,並開辟出一片緩存 區存儲經過多種濾波類型的預測值
l getPredictorPtr:根據不同模式選擇經過不同類型濾波的預測集
l predIntraLumaAng: 對亮度信號進行預測,里面會調用xPredIntraPlanar,xPredIntraAng以及xDCPredFiltering
l predIntraChromaAng: 對色度信號進行預測,里面會調用xPredIntraPlanar和xPredIntraAng
l xPredIntraPlanar: planar模式的預測
l xPredIntraAng: 角度的方向性預測
l xDCPredFiltering: 對DC的預測值進行濾波
l getLumaRecPixels: 獲取亮度的重建值,為進行LM模式的預測做准備
l predLMIntraChroma:對LM模式進行預測,即利用亮度的相關性,對色度進行預測
6.1.2 幀間
l getInterMergeCandidates: 獲取merge的候選運動參數集
l motionCompensation:進行運動補償
l xMotionEstimation:進行運動估計
l xEstimateMvPredAMVP:選出代價最小的MVP
l xCheckBestMVP:在知道MV的情況下比較各個MVP的優劣,並保存最優的
l xMergeEstimation:在inter模式時也可以使用merge模式的運動估計方法,這個函數用於計算這種情況時的代價
6.2 變換
l transformNxN:會調用xT和xQuant函數
l invtransformNxN:會調用xDeQuant和xIT函數
l xT: 對殘差信號進行變換
l xQuant:對變換系數進行量化
l xDeQuant:反量化
l xIT:反變換
6.3 熵編碼
在這節中主要介紹編碼端為算RD代價而設計的熵編碼函數,實際的熵編碼函數在后面的章節中進行介紹
主要函數:
6.3.1 幀內熵編碼
l xEncIntraHeader:編碼intra的一些頭部信息,主要包括:模式號,PU的分割類型,PCM標志,如果是B或P slice,還包括skip的標志位和編碼模式的類型
l xEncSubdivCbfQT:會編碼Cbf和TU分割的標志位
l xEncCoeffQT:編碼每個TU的系數
l encodeCoeffNxN:調用codeCoeffNxN來編碼每個TU的殘差系數
l encodeTransformSubdivFlag:調用codeTransformSubdivFlag來編碼TU分割的標志,是否繼續分割
l encodeQtCbf:編碼cbf標志位,檢查是否有非零的系數
l encodePredMode:編碼所采用的編碼模式
l encodePartSize:編碼PU的分割類型
l encodeIntraDirModeLuma:編碼PU的亮度模式號,這里引入了3MPM的機制,具體可參考提案H0238
l encodeIntraDirModeChroma:編碼PU的色度模式號
6.3.2 幀間熵編碼
l encodePredMode:編碼CU所采用的模式,主要決定是inter還是intra
l encodePartSize:編碼PU的分割類型
l encodePredInfo:編碼運動參數
(1) merge的標志位來區別是否采用merge模式,具體函數:encodeMergeFlag
然后分(2)和(3)兩種情況。
(2) merge模式:只需傳輸運動候選集的索引,具體函數:encodeMergeIndex
(3) 正常的inter模式
A. encodeInterDirPU:編碼幀間的預測方向,前向,后向,或多方向
B. encodeRefFrmIdxPU: 編碼參考幀索引
C. encodeMvdPU:編碼MV的殘差MVD
D. encodeMVPIdxPU: 編碼MVP的索引
7. EncoderCU
HEVC以LCU為基本單位,所以在進行熵編碼時也是以LCU為單位進行的EncodeCU會調用從而對每個CU進行編碼,如下圖所示,在xEncodeCU中會調用如下幾個函數:
encodeSkipFlag編碼是否是skip模式
encodeMergeIndex如果是skip模式會編碼選用哪套MVP的參數
encodePredMode編碼CU的模式,是intra還是inter
encodePartSize編碼CU中的PU的類型
encodeIPCMInfo 如果選用了PCM模式會編碼PCM模式的信息
encodePredInfo編碼預測的信息,如果是幀內,編碼模式號,如果是幀間,則編碼運動信息
encodeCoeff編碼殘差系數
encodeCoeff中會編碼TU的分割標志位,cbf和殘差系數的信息
而具體的信息可以參照3.3節
8. 一些主要變量和數據結構的說明:
8.1 TComDataCU:LCU及其子CU的數據結構,存儲了一個LCU所有的相關信息,里面重要的數據結構包括:
l m_uiAbsIdxInLCU:當前CU在LCU中的位置,位置用Z掃描順序
l m_puhWidth: CU的寬度
l m_puhHeight:CU的高度
l m_puhDepth: CU所處的深度
l m_pePartSize: PU的類型
l m_pePredMode:編碼模式
l m_pcTrCoeffY,m_pcTrCoeffCb,m_pcTrCoeffCr:量化后的系數
l m_puhLumaIntraDir:亮度的模式信息
l m_puhChromaIntraDir:色度的模式信息
l m_puhInterDir:幀間的預測方向
l m_apiMVPIdx:MVP索引
l m_apiMVPNum:MVP的候選數
以上的數據結構都是以動態存儲來分配空間,一般只有一維,這一維具體取值的含義就是CU里面的每個對應的4x4的小塊的信息,而開辟的數目就是CU所包含的4x4的數目,而在實際編碼時也是編碼了這些信息。
需要着重說明2點
(1) m_uiCUAddr是一個LCU在slice中的位置,是raster的掃描順序
(2) m_uiAbsIdxInLCU是表明CU在LCU中的位置,Z掃描順序,最小單位為1,代表
其中的一個4x4子塊,Z掃描順序如下圖所示
(3) Z掃描轉換,如下圖所示,展示了一個CU內部的Z掃描的順序,在hevc中,Z掃描順序是以4x4為基本單位的,一個具有默認尺寸的LCU,具有256個基本單元
8.2 RDO時所用到的主要臨時變量
l m_ppcQTTempCoeffY,m_ppcQTTempCoeffCb,m_ppcQTTempCoeffCr:RQT時每層的量化系數,都保存在此,是為了確定最終分割后可以很容易的獲取最優值
l m_pcQTTempCoeffY,m_pcQTTempCoeffCb,m_pcQTTempCoeffCr:CU層的量化系數暫存地,只有幀間編碼時才會用到,是中間變量
l m_pcQTTempTComYuv: 重建視頻的暫存緩沖區
l m_puhQTTempCbf: cbf的暫存
l m_puhQTTempTrIdx:變換層數的暫存
l m_ppcBestCU:存儲每層最優(RD代價最小)的CU的信息
l m_ppcTempCU: 存儲每層CU的信息的臨時變量
l m_ppcPredYuvBest: 存儲每層最優的預測值
l m_ppcResiYuvBest:存儲每層最優的殘差值
l m_ppcRecoYuvBest:存儲每層最優的重建值
l m_ppcPredYuvTemp:存儲每層預測值的臨時變量
l m_ppcResiYuvTemp:存儲每層殘差值的臨時變量
l m_ppcRecoYuvTemp:存儲每層重建值的臨時變量
l m_ppcOrigYuv::存儲每層對應的原始值
8.3 yuv的存儲的關系
8.3.1 TComYuv數據結構
由m_apiBufY,m_apiBufU以及m_apiBufV三個buffer組成,通用的yuv數據結構,存儲是yuv的亮度和色度信息
8.3.2 TComPicYuv數據結構
圖像層級的yuv數據結構,存儲的是一幀的yuv信息,主要用於ALF和去方塊濾波等處理的過程中
TComYuv的類型的變量存儲的是RDO時的值,最優的信息要存在TComPicYuv中,便於輸出和進行全局處理
9. 解碼端的簡單說明
9.1 xDecodeCU: 與xEncodeCU類似,進行LCU的讀取碼流並存至變量的工作,可以理解為與xEncodeCU的逆過程。涉及的函數如下:
l decodeSkipFlag:解碼skip的flag,看是不是skip模式
l decodePredMode:解碼編碼模式
l decodePartSize: 解碼PU分割的類型
l decodePredInfo:解碼預測信息,幀內就是解碼模式信息,幀間是解碼運動信息
l decodeCoeff:解碼量化系數
9.2 xDecompressCU: 具體的任務為重建這個LCU,涉及的函數如下:
9.2.1 xReconInter
負責inter部分的重建,主要函數如下:
l xDecodeInterTexture:分別對YUV分量調用invRecurTransformNxN
l invRecurTransformNxN:對特定分量進行TU的反量化和反變換
l addClip:得到殘差后會加上預測值形成重建指
l copyPartToPartYuv:如果系數全是零,則直接將重構值賦值為預測值
9.2.2 xReconIntraQT
負責intra部分的重建
xIntraLumaRecQT:亮度信息的重建,會對每個TU調用xIntraRecLumaBlk
xIntraRecLumaBlk:TU的亮度信息反量化及重建工作
xIntraChromaRecQT:色度信息的重建,會對每個TU調用xIntraRecChromaBlk:TU的色度信息反量化及重建工作