xEncodeCU是由encodeCtu调用,其作用是从CTU开始迭代对每个CU进行编码。注意,xEncodeCU是在最优分块已经划分完成后进行编码时使用的,在xCompressCU中没有使用。
xEncodeCU会调用以下函数来完成各部分的编码:
encodeSplitFlag编码块是否划分
encodeSkipFlag编码是否是skip模式
encodeMergeIndex如果是skip模式,编码merge索引
encodePredMode编码CU的模式,是intra还是inter
encodePartSize编码CU中的PU的类型
encodeIPCMInfo 如果选用了PCM模式会编码PCM模式的信息
encodePredInfo编码预测的信息,如果是帧内,编码预测方向,如果是帧间,编码MV和参考索引
encodeCoeff编码残差系数
encodeCoeff中会编码TU的分割标志位,cbf和残差系数的信息
/** encode a CU block recursively
* \param pcCU
* \param uiAbsPartIdx
* \param uiDepth
* \returns Void
*/
//迭代对CU编码
Void TEncCu::xEncodeCU( TComDataCU* pcCU, UInt uiAbsPartIdx, UInt uiDepth )
{
TComPic *const pcPic = pcCU->getPic(); //当前图像
TComSlice *const pcSlice = pcCU->getSlice(); //当前Slice
const TComSPS &sps =*(pcSlice->getSPS()); //SPS
const TComPPS &pps =*(pcSlice->getPPS()); //PPS
const UInt maxCUWidth = sps.getMaxCUWidth(); //最大CU宽度
const UInt maxCUHeight = sps.getMaxCUHeight(); //最大CU高度
Bool bBoundary = false; //边界
UInt uiLPelX = pcCU->getCUPelX() + g_auiRasterToPelX[ g_auiZscanToRaster[uiAbsPartIdx] ]; //起始x坐标
const UInt uiRPelX = uiLPelX + (maxCUWidth>>uiDepth) - 1; //结束x坐标
UInt uiTPelY = pcCU->getCUPelY() + g_auiRasterToPelY[ g_auiZscanToRaster[uiAbsPartIdx] ]; //起始y坐标
const UInt uiBPelY = uiTPelY + (maxCUHeight>>uiDepth) - 1; //结束y坐标
if( ( uiRPelX < sps.getPicWidthInLumaSamples() ) && ( uiBPelY < sps.getPicHeightInLumaSamples() ) )
{
m_pcEntropyCoder->encodeSplitFlag( pcCU, uiAbsPartIdx, uiDepth ); //编码划分Flag
}
else
{
bBoundary = true;
}
if( ( ( uiDepth < pcCU->getDepth( uiAbsPartIdx ) ) && ( uiDepth < sps.getLog2DiffMaxMinCodingBlockSize() ) ) || bBoundary )
{
UInt uiQNumParts = ( pcPic->getNumPartitionsInCtu() >> (uiDepth<<1) )>>2;
if( uiDepth == pps.getMaxCuDQPDepth() && pps.getUseDQP()) //如果使用DQP,置flag
{
setdQPFlag(true);
}
if( uiDepth == pps.getPpsRangeExtension().getDiffCuChromaQpOffsetDepth() && pcSlice->getUseChromaQpAdj()) //色度QP自适应
{
setCodeChromaQpAdjFlag(true);
}
for ( UInt uiPartUnitIdx = 0; uiPartUnitIdx < 4; uiPartUnitIdx++, uiAbsPartIdx+=uiQNumParts ) //遍历四个分块编码
{
uiLPelX = pcCU->getCUPelX() + g_auiRasterToPelX[ g_auiZscanToRaster[uiAbsPartIdx] ]; //块的起始x坐标
uiTPelY = pcCU->getCUPelY() + g_auiRasterToPelY[ g_auiZscanToRaster[uiAbsPartIdx] ]; //块的起始y坐标
if( ( uiLPelX < sps.getPicWidthInLumaSamples() ) && ( uiTPelY < sps.getPicHeightInLumaSamples() ) )
{
xEncodeCU( pcCU, uiAbsPartIdx, uiDepth+1 );
}
}
return;
}
if( uiDepth <= pps.getMaxCuDQPDepth() && pps.getUseDQP()) //如果使用DQP,置flag
{
setdQPFlag(true);
}
if( uiDepth <= pps.getPpsRangeExtension().getDiffCuChromaQpOffsetDepth() && pcSlice->getUseChromaQpAdj())
{
setCodeChromaQpAdjFlag(true);
}
if (pps.getTransquantBypassEnabledFlag()) //默认false
{
m_pcEntropyCoder->encodeCUTransquantBypassFlag( pcCU, uiAbsPartIdx );
}
if( !pcSlice->isIntra() ) //帧间编码编码skip flag
{
m_pcEntropyCoder->encodeSkipFlag( pcCU, uiAbsPartIdx );
}
if( pcCU->isSkipped( uiAbsPartIdx ) ) //编码merge索引
{
m_pcEntropyCoder->encodeMergeIndex( pcCU, uiAbsPartIdx );
finishCU(pcCU,uiAbsPartIdx);
return;
}
m_pcEntropyCoder->encodePredMode( pcCU, uiAbsPartIdx ); //编码预测模式
m_pcEntropyCoder->encodePartSize( pcCU, uiAbsPartIdx, uiDepth ); //编码PU类型
if (pcCU->isIntra( uiAbsPartIdx ) && pcCU->getPartitionSize( uiAbsPartIdx ) == SIZE_2Nx2N ) //帧内SIZE_2Nx2N时,需要判断是否编码PCM信息
{
m_pcEntropyCoder->encodeIPCMInfo( pcCU, uiAbsPartIdx );
if(pcCU->getIPCMFlag(uiAbsPartIdx))
{
// Encode slice finish
finishCU(pcCU,uiAbsPartIdx);
return;
}
}
// prediction Info ( Intra : direction mode, Inter : Mv, reference idx )
m_pcEntropyCoder->encodePredInfo( pcCU, uiAbsPartIdx ); //编码预测信息:帧内为预测方向,帧间为MV和参考索引
// Encode Coefficients
//编码系数
Bool bCodeDQP = getdQPFlag();
Bool codeChromaQpAdj = getCodeChromaQpAdjFlag();
m_pcEntropyCoder->encodeCoeff( pcCU, uiAbsPartIdx, uiDepth, bCodeDQP, codeChromaQpAdj );
setCodeChromaQpAdjFlag( codeChromaQpAdj );
setdQPFlag( bCodeDQP );
// --- write terminating bit ---
finishCU(pcCU,uiAbsPartIdx);
}