CompressGOP->CompressSlice->EncodeSlice
Void TEncGOP::compressGOP( Int iPOCLast, Int iNumPicRcvd, TComList<TComPic*>& rcListPic,
TComList<TComPicYuv*>& rcListPicYuvRecOut, std::list<AccessUnit>& accessUnitsInGOP,
Bool isField, Bool isTff, const InputColourSpaceConversion snr_conversion, const Bool printFrameMSE )
{
.
.
.
for(UInt nextCtuTsAddr = 0; nextCtuTsAddr < numberOfCtusInFrame; )
{
m_pcSliceEncoder->precompressSlice( pcPic );
m_pcSliceEncoder->compressSlice ( pcPic, false, false);
.
.
.
}
Void TEncSlice::compressSlice( TComPic* pcPic, const Bool bCompressEntireSlice, const Bool bFastDeltaQP )//每个slice都会调用compressslice来对其进行预测变换量化选出最优参数
{
.
.
.
}
Void TEncSlice::encodeSlice ( TComPic* pcPic, TComOutputBitstream* pcSubstreams, UInt &numBinsCoded )//slice通过最后调用encodeslice对其进行熵编码
{
.
.
.
}
CompressSlice->CompressCtu->xCompressCU->xCheckRDCostInter->predInterSearch->xMotionEstimation->xPatternSearch
Void TEncSlice::compressSlice( TComPic* pcPic, const Bool bCompressEntireSlice, const Bool bFastDeltaQP )//每个slice都会调用compressslice来对其进行预测变换量化选出最优参数
{
.
.
.
// run CTU trial encoder
m_pcCuEncoder->compressCtu( pCtu );//就是一个CTU的编码,包括CU的划分,PU模式的决定,TU的划分
.
.
.
}
Void TEncCu::compressCtu(TComDataCU* pCtu)
{
.
.
.
//这里进入xCompressCU函数,这里的第三个参数是 CU的分割深度。64x64的深度为0,以此类推,最小的CU为8x8深度为3。
//把一个slice内部的图像划分为K个LCU,同时这K个LCU是以raster的顺序来进行扫描的。LCU的尺寸默认为64x64,可以通过配置文件中的MaxCUWidth,MaxCUHeight来进行设置。
//而每个LCU会调用如下的compressCU函数去决定编码的参数。 而LCU是采用四叉树的表示结构,每个LCU会被递归的划分为4个子CU,
//并根据RD代价来确定是否进行划分,而每个LCU相当于树的第1层,所以他会调用xCompressCU。且在xCompressCU中会对每个子CU递归的调用xCompressCU,
//然而在每次xCompressCU时,会对当前CU进行intra模式的测试,如果是B或Pslice,还对其进行merge和inter模式的测试。下面分别介绍intra,inter和merge相关的代码
xCompressCU(m_ppcBestCU[0], m_ppcTempCU[0], 0 DEBUG_STRING_PASS_INTO(sDebug))
.
.
.
}
Void TEncCu::xCompressCU( TComDataCU*& rpcBestCU, TComDataCU*& rpcTempCU, const UInt uiDepth )
#endif//总结,其实xCompressCU的作用就是从LCU开始深度遍历,计算每一个depth上最优的模式,再综合比较各个depth上最优的模式,选出最优的模式
{
.
.
.
//*****************************************帧间模式选择***************************//
// do inter modes, SKIP and 2Nx2N // do inter modes, SKIP and 2Nx2N
/*
** 在处理所有的其他模式之前,先处理帧间skip和2Nx2N的模式
** 特别是对于2Nx2N的划分,要分两次处理:
** 1、尝试merge模式——xCheckRDCostMerge2Nx2N
** 2、尝试普通的帧间预测(即AMVP)——xCheckRDCostInter
*/
if(m_pcEncCfg->getUseEarlySkipDetection())//使用earlyDetectionSkipMode优化算法
// 更新为Merge模式的代价
xCheckRDCostMerge2Nx2N( rpcBestCU, rpcTempCU DEBUG_STRING_PASS_INTO(sDebug), &earlyDetectionSkipMode );//by Merge for inter_2Nx2N
if(!m_pcEncCfg->getUseEarlySkipDetection())//不使用earlyDetectionSkipMode优化算法
// do inter modes, NxN, 2NxN, and Nx2N
/*
** 普通的帧间预测(普通的帧间预测就是AMVP)开始:
** 注意:这里不再处理merge模式和普通帧间的2Nx2N划分模式,
** 这是因为前面已经处理过2Nx2N的划分模式了,merge模式只对于2Nx2N的划分才有效
** 因此下面的处理是没有merge模式和2Nx2N的划分模式的
*/
if( rpcBestCU->getSlice()->getSliceType() != I_SLICE )//非I帧
{
// NxN 划分为4个PU
if(!( (rpcBestCU->getWidth(0)==8) && (rpcBestCU->getHeight(0)==8) ))//块尺寸不能为8x8
{
if( uiDepth == sps.getLog2DiffMaxMinCodingBlockSize() && doNotBlockPu)
{
//计算NxN模式代价并比较
xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_NxN DEBUG_STRING_PASS_INTO(sDebug) );
rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
}
}
//计算Nx2N
if(doNotBlockPu)
{
//计算Nx2N代价并比较 划分为2个PU
xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_Nx2N
DEBUG_STRING_PASS_INTO(sDebug) );
rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) ==
SIZE_Nx2N )
{
doNotBlockPu = rpcBestCU->getQtRootCbf( 0 ) != 0;
}
}
//计算2NxN代价并比较
if(doNotBlockPu)
{
xCheckRDCostInter ( rpcBestCU, rpcTempCU, SIZE_2NxN DEBUG_STRING_PASS_INTO(sDebug) );
rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_2NxN)
{
doNotBlockPu = rpcBestCU->getQtRootCbf( 0 ) != 0;
}
}
//尝试非对称尺寸
//! Try AMP (SIZE_2NxnU, SIZE_2NxnD, SIZE_nLx2N, SIZE_nRx2N)
// 接下来是2NxnU、2NxnD、nLx2N、nRx2N的划分模式的处理
/*
** 接下来的处理有点讲究:
** 1、首先测试AMP_ENC_SPEEDUP宏(表示是否加快编码速度)是否开启
** 2、如果AMP_ENC_SPEEDUP宏开启
** (1)默认情况下,如果TestAMP_Hor、TestAMP_Ver为真,那么可以处理2NxnU、2NxnD、nLx2N、nRx2N这四种模式
** (2)如果TestAMP_Hor、TestAMP_Ver为假,但是开启了AMP_MRG宏,而且TestMergeAMP_Hor、TestMergeAMP_Ver为真,那么还是可以处理2NxnU、2NxnD、nLx2N、nRx2N这四种模式
** 否则不再处理2NxnU、2NxnD、nLx2N、nRx2N这四种模式
** (3)由于上面会根据一些条件来判断是否需要处理2NxnU、2NxnD、nLx2N、nRx2N这四种模式,因此某些时候速度会快一点
** 3、如果AMP_ENC_SPEEDUP关闭
** 那么直接处理2NxnU、2NxnD、nLx2N、nRx2N这四种模式,因为没有了条件限制,这四种模式都要测试,因此,速度会慢一点
*/
//! Do horizontal AMP // TestAMP_Hor为真的话,可以使用2NxnU和2NxnD这两种划分模式
if ( bTestAMP_Hor )
{// 处理2NxnU模式
if(doNotBlockPu)
{
xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2NxnU DEBUG_STRING_PASS_INTO(sDebug) );
rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_2NxnU )
{
doNotBlockPu = rpcBestCU->getQtRootCbf( 0 ) != 0;
}
}
if(doNotBlockPu) // 处理2NxnD模式
{
xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2NxnD DEBUG_STRING_PASS_INTO(sDebug) );
rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_2NxnD )
{
doNotBlockPu = rpcBestCU->getQtRootCbf( 0 ) != 0;
}
}
}
#if AMP_MRG // TestMergeAMP_Hor为真的话可以使用2NxnU、2NxnD这两种模式
else if ( bTestMergeAMP_Hor )
{
if(doNotBlockPu)// 处理2NxnU模式
{
xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2NxnU DEBUG_STRING_PASS_INTO(sDebug), true );
rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_2NxnU )
{
doNotBlockPu = rpcBestCU->getQtRootCbf( 0 ) != 0;
}
}
if(doNotBlockPu)// 处理2NxnD模式
{
xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2NxnD DEBUG_STRING_PASS_INTO(sDebug), true );
rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_2NxnD )
{
doNotBlockPu = rpcBestCU->getQtRootCbf( 0 ) != 0;
}
}
}
#endif
//! Do horizontal AMP// TestAMP_Ver为真可以处理nLx2N、nRx2N两种模式
if ( bTestAMP_Ver )
{
if(doNotBlockPu)// 处理nLx2N模式
{
xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_nLx2N DEBUG_STRING_PASS_INTO(sDebug) );
rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_nLx2N )
{
doNotBlockPu = rpcBestCU->getQtRootCbf( 0 ) != 0;
}
}
if(doNotBlockPu) // 处理nRx2N模式
{
xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_nRx2N DEBUG_STRING_PASS_INTO(sDebug) );
rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
}
}
#if AMP_MRG // TestMergeAMP_Ver为真可以处理nLx2N、nRx2N模式
else if ( bTestMergeAMP_Ver )
{
if(doNotBlockPu)// 处理nLx2N模式
{
xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_nLx2N DEBUG_STRING_PASS_INTO(sDebug), true );
rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_nLx2N )
{
doNotBlockPu = rpcBestCU->getQtRootCbf( 0 ) != 0;
}
}
if(doNotBlockPu)// 处理nRx2N模式
{
xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_nRx2N DEBUG_STRING_PASS_INTO(sDebug), true );
rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
}
}
}
xCompressCU的作用:从LCU开始深度遍历,计算每一个深度上最优的模式,再综合比较各个深度上最优的模式,选出最优的模式
xCompressCU中的帧间每一个模式的选择都会调用xCheckRDCostInter函数
xCheckRDCostInter主要的功能: 进行帧间搜索,计算当前CU划分模式的RDcost
xCheckRDCostInter->predInterSearch->encodeResAndCalcRdInterCU->xCheckDQP->xCheckBestMode
predInterSearch:帧间搜索
encodeResAndCalcRdInterCU:计算残差和RDcost
xCheckDQP:deltaQP检测
xCheckBestMode:比较RDcost设置最优模式