在之前 HEVC代码学习38:decompressSlice函数 学习中提到,解码slice会遍历所有CTU,调用decodeCtu
和decompressCtu
解码每一个CTU。下面就来学习一下decodeCtu
和xDecodeCU
函数。
decodeCtu
该函数是解码CTU的入口函数,有两个输入参数:待解码的CTU和一个标识是否是slice最后一个CTU的flag。代码很短,其中调用xDecodeCU
来完成真正的CTU解码工作。
代码分析如下:
/**
Parse a CTU.
\param pCtu [in/out] pointer to CTU data structure
\param isLastCtuOfSliceSegment [out] true, if last CTU of the slice segment
*/
//解码CTU
Void TDecCu::decodeCtu( TComDataCU* pCtu, Bool& isLastCtuOfSliceSegment )
{
//delta QP
if ( pCtu->getSlice()->getPPS()->getUseDQP() )
{
setdQPFlag(true);
}
//色度分量QP自适应
if ( pCtu->getSlice()->getUseChromaQpAdj() )
{
setIsChromaQpAdjCoded(true);
}
// start from the top level CU
//解码CU
xDecodeCU( pCtu, 0, 0, isLastCtuOfSliceSegment);
}
xDecodeCU
该函数通过迭代完成一个CTU的所有CU的解码工作,主要是各种预测信息的解码,是xEncodeCU
(详见
HEVC代码学习35:xEncodeCU函数)的逆处理。有4个输入参数:当前CU,分块索引,深度和标识是否是slice最后一个CTU的flag。
主要流程如下:
1.初始化,获取当前CU的图像、SPS、PPS、最大CU宽度高度等信息。
2.计算CU边界坐标。
3.解码split flag
4.判断深度是否小于四叉树划分深度和是否是边界位置。如果为真,迭代解码四个子CU;否则执行步骤5。
5.解码当前CU的各种信息:
5.1 解码skip flag,如果skip flag为true,解码并构造merge列表,取出merge idx对应的MV,返回。构建merge候选列表时,根据merge索引只构建到对应的MV候选。比如,merge idx为1,只构建0、1位置两个的候选。
5.2 解码预测模式和尺寸。如果是帧内预测,且尺寸为2Nx2N,解码PCM模式信息,如果PCM flag为真,返回。
5.3 不是skip和PCM模式,调用decodePredInfo
解码预测模式信息(帧内预测、帧间预测)以及系数。
代码分析:
//! decode CU block recursively
//迭代解码CU
Void TDecCu::xDecodeCU( TComDataCU*const pcCU, const UInt uiAbsPartIdx, const UInt uiDepth, Bool &isLastCtuOfSliceSegment)
{
//初始化
TComPic* pcPic = pcCU->getPic();
const TComSPS &sps = pcPic->getPicSym()->getSPS();
const TComPPS &pps = pcPic->getPicSym()->getPPS();
const UInt maxCuWidth = sps.getMaxCUWidth();
const UInt maxCuHeight= sps.getMaxCUHeight();
UInt uiCurNumParts = pcPic->getNumPartitionsInCtu() >> (uiDepth<<1);
UInt uiQNumParts = uiCurNumParts>>2;
//边界
Bool bBoundary = false;
UInt uiLPelX = pcCU->getCUPelX() + g_auiRasterToPelX[ g_auiZscanToRaster[uiAbsPartIdx] ];
UInt uiRPelX = uiLPelX + (maxCuWidth>>uiDepth) - 1;
UInt uiTPelY = pcCU->getCUPelY() + g_auiRasterToPelY[ g_auiZscanToRaster[uiAbsPartIdx] ];
UInt uiBPelY = uiTPelY + (maxCuHeight>>uiDepth) - 1;
//解码split flag
if( ( uiRPelX < sps.getPicWidthInLumaSamples() ) && ( uiBPelY < sps.getPicHeightInLumaSamples() ) )
{
m_pcEntropyDecoder->decodeSplitFlag( pcCU, uiAbsPartIdx, uiDepth );
}
else
{
bBoundary = true;
}
//当前深度小于四叉树划分深度或是边界位置,迭代解码四个子CU
if( ( ( uiDepth < pcCU->getDepth( uiAbsPartIdx ) ) && ( uiDepth < sps.getLog2DiffMaxMinCodingBlockSize() ) ) || bBoundary )
{
UInt uiIdx = uiAbsPartIdx;
//delta QP
if( uiDepth == pps.getMaxCuDQPDepth() && pps.getUseDQP())
{
setdQPFlag(true);
pcCU->setQPSubParts( pcCU->getRefQP(uiAbsPartIdx), uiAbsPartIdx, uiDepth ); // set QP to default QP
}
//色度QP自适应
if( uiDepth == pps.getPpsRangeExtension().getDiffCuChromaQpOffsetDepth() && pcCU->getSlice()->getUseChromaQpAdj() )
{
setIsChromaQpAdjCoded(true);
}
//迭代解码四个子CU
for ( UInt uiPartUnitIdx = 0; uiPartUnitIdx < 4; uiPartUnitIdx++ )
{
uiLPelX = pcCU->getCUPelX() + g_auiRasterToPelX[ g_auiZscanToRaster[uiIdx] ];
uiTPelY = pcCU->getCUPelY() + g_auiRasterToPelY[ g_auiZscanToRaster[uiIdx] ];
if ( !isLastCtuOfSliceSegment && ( uiLPelX < sps.getPicWidthInLumaSamples() ) && ( uiTPelY < sps.getPicHeightInLumaSamples() ) )
{
//自调用完成子CU解码。
xDecodeCU( pcCU, uiIdx, uiDepth+1, isLastCtuOfSliceSegment );
}
else
{
pcCU->setOutsideCUPart( uiIdx, uiDepth+1 );
}
uiIdx += uiQNumParts;
}
//delta QP
if( uiDepth == pps.getMaxCuDQPDepth() && pps.getUseDQP())
{
if ( getdQPFlag() )
{
UInt uiQPSrcPartIdx = uiAbsPartIdx;
pcCU->setQPSubParts( pcCU->getRefQP( uiQPSrcPartIdx ), uiAbsPartIdx, uiDepth ); // set QP to default QP
}
}
return;
}
if( uiDepth <= pps.getMaxCuDQPDepth() && pps.getUseDQP())
{
setdQPFlag(true);
pcCU->setQPSubParts( pcCU->getRefQP(uiAbsPartIdx), uiAbsPartIdx, uiDepth ); // set QP to default QP
}
if( uiDepth <= pps.getPpsRangeExtension().getDiffCuChromaQpOffsetDepth() && pcCU->getSlice()->getUseChromaQpAdj() )
{
setIsChromaQpAdjCoded(true);
}
//解码TransquantBypassFlag
if (pps.getTransquantBypassEnableFlag())
{
m_pcEntropyDecoder->decodeCUTransquantBypassFlag( pcCU, uiAbsPartIdx, uiDepth );
}
// decode CU mode and the partition size
//帧间模式解码skip flag
if( !pcCU->getSlice()->isIntra())
{
m_pcEntropyDecoder->decodeSkipFlag( pcCU, uiAbsPartIdx, uiDepth );
}
//skip模式
if( pcCU->isSkipped(uiAbsPartIdx) )
{
m_ppcCU[uiDepth]->copyInterPredInfoFrom( pcCU, uiAbsPartIdx, REF_PIC_LIST_0 );
m_ppcCU[uiDepth]->copyInterPredInfoFrom( pcCU, uiAbsPartIdx, REF_PIC_LIST_1 );
//merge MV候选列表
TComMvField cMvFieldNeighbours[MRG_MAX_NUM_CANDS << 1]; // double length for mv of both lists
//merge 方向候选列表
UChar uhInterDirNeighbours[MRG_MAX_NUM_CANDS];
Int numValidMergeCand = 0;
for( UInt ui = 0; ui < m_ppcCU[uiDepth]->getSlice()->getMaxNumMergeCand(); ++ui )
{
uhInterDirNeighbours[ui] = 0;
}
//解码merge index
m_pcEntropyDecoder->decodeMergeIndex( pcCU, 0, uiAbsPartIdx, uiDepth );
UInt uiMergeIndex = pcCU->getMergeIndex(uiAbsPartIdx);
//获取merge候选列表
m_ppcCU[uiDepth]->getInterMergeCandidates( 0, 0, cMvFieldNeighbours, uhInterDirNeighbours, numValidMergeCand, uiMergeIndex );
pcCU->setInterDirSubParts( uhInterDirNeighbours[uiMergeIndex], uiAbsPartIdx, 0, uiDepth );
TComMv cTmpMv( 0, 0 );
//遍历list0和list1获取MV
for ( UInt uiRefListIdx = 0; uiRefListIdx < 2; uiRefListIdx++ )
{
if ( pcCU->getSlice()->getNumRefIdx( RefPicList( uiRefListIdx ) ) > 0 )
{
pcCU->setMVPIdxSubParts( 0, RefPicList( uiRefListIdx ), uiAbsPartIdx, 0, uiDepth);
pcCU->setMVPNumSubParts( 0, RefPicList( uiRefListIdx ), uiAbsPartIdx, 0, uiDepth);
pcCU->getCUMvField( RefPicList( uiRefListIdx ) )->setAllMvd( cTmpMv, SIZE_2Nx2N, uiAbsPartIdx, uiDepth );
//从merge候选中取出对应候选MV
pcCU->getCUMvField( RefPicList( uiRefListIdx ) )->setAllMvField( cMvFieldNeighbours[ 2*uiMergeIndex + uiRefListIdx ], SIZE_2Nx2N, uiAbsPartIdx, uiDepth );
}
}
//结束编码CU,处理delta QP和end-of-slice flag
xFinishDecodeCU( pcCU, uiAbsPartIdx, uiDepth, isLastCtuOfSliceSegment );
return;
}
//非skip模式
//解码预测模式和预测块尺寸
m_pcEntropyDecoder->decodePredMode( pcCU, uiAbsPartIdx, uiDepth );
m_pcEntropyDecoder->decodePartSize( pcCU, uiAbsPartIdx, uiDepth );
//帧内且尺寸为2Nx2N
if (pcCU->isIntra( uiAbsPartIdx ) && pcCU->getPartitionSize( uiAbsPartIdx ) == SIZE_2Nx2N )
{
//解码I_PCM信息
m_pcEntropyDecoder->decodeIPCMInfo( pcCU, uiAbsPartIdx, uiDepth );
if(pcCU->getIPCMFlag(uiAbsPartIdx))
{
xFinishDecodeCU( pcCU, uiAbsPartIdx, uiDepth, isLastCtuOfSliceSegment );
return;
}
}
// prediction mode ( Intra : direction mode, Inter : Mv, reference idx )
//解码预测模式(帧内:方向模式,帧间:MV和参考索引)
m_pcEntropyDecoder->decodePredInfo( pcCU, uiAbsPartIdx, uiDepth, m_ppcCU[uiDepth]);
// Coefficient decoding
//解码系数
Bool bCodeDQP = getdQPFlag();
Bool isChromaQpAdjCoded = getIsChromaQpAdjCoded();
m_pcEntropyDecoder->decodeCoeff( pcCU, uiAbsPartIdx, uiDepth, bCodeDQP, isChromaQpAdjCoded );
setIsChromaQpAdjCoded( isChromaQpAdjCoded );
setdQPFlag( bCodeDQP );
xFinishDecodeCU( pcCU, uiAbsPartIdx, uiDepth, isLastCtuOfSliceSegment );
}