本文首先对HM中帧间预测的基本流程作简要介绍,接着对代码中关键变量的用途作出说明,最后以源代码+注释的形式进行具体分析。
备注:这位大神的博客对楼主帮助很大,解决了我的不少疑惑,最后才能顺利写下这篇博客。大神博客地址:(https://blog.csdn.net/NB_vol_1/article/details/55272434)。对帧间预测基本概念还不是很熟悉的同学,可以先看看大神的博客。

HM中帧间预测的基本流程

HEVC中帧间预测支持多种划分模式:Merge(Skip是特殊的Merge),2Nx2N,NxN, 2NxN, Nx2N以及AMP:2NxnU,2NxnD, nLx2N,nRx2N。HM中便是按照固定的顺序对上述帧间划分模式进行评估,最后选出最优的帧间模式(最后还会对帧内模式、PCM模式进行评估)。

xCompressCU中的关键变量

HM中进行帧间预测的入口函数是xCompressCU,当然函数里面不仅仅是做了帧间预测这一件事(还有帧内预测、PCM等)。以下列出函数体中的关键变量及其用途。
bool doNotBlockPu:默认为true。值为false时,跳过对后续帧间划分模式的评估。只有使能cbf快速模式(默认关闭)时,doNotBlockPu的值才会被设为false。注:cbf快速模式–cbf值为0时,跳过对当前深度后续剩余划分模式的评估,cbf值为0,表明残差值为0.
bool earlyDetectionSkipMode:默认为false。值为true时,跳过对剩余划分模式以及四叉树递归划分的评估操作。只有使能EarlySkipDetection模式(默认关闭),earlyDetectionSkipMode才会被设为true。
AMP相关变量:
bool bTestAMP_Hor:默认为false。值为true时,对水平AMP模式进行评估(2NxnU,2NxnD)
bool bTestAMP_Ver:默认为false。值为true时,对垂直AMP模式进行评估(nLx2N,nRx2N)
bool bTestMergeAMP_Hor:默认为false。值为true时,对水平AMP模式进行Merge评估,即选出最优的Merge候选项
bool bTestMergeAMP_Ver:默认为false。值为true时,对垂直AMP模式进行Merge评估。
rpcTempCU:当前评估的CU模式
rpcBestCU:当前最优CU模式

具体代码分析

注意:此代码段是从HM16.20源代码TEncCu.cpp中完整拷贝过来的,里面加入了个人的阅读注释。这里对于与帧间预测无关的代码未做解释说明。楼主对代码的注释可能会存在理解偏差,望读者可以指出来,一起交流探讨~毕竟楼主对于HM源代码研究不多,不少地方也存在疑惑。希望有大佬同仁一起学习交流,共同进步!

// ====================================================================================================================
// Protected member functions
// ====================================================================================================================
/** Compress a CU block recursively with enabling sub-CTU-level delta QP*  - for loop of QP value to compress the current CU with all possible QP
*/
#if AMP_ENC_SPEEDUP
Void TEncCu::xCompressCU( TComDataCU*& rpcBestCU, TComDataCU*& rpcTempCU, const UInt uiDepth DEBUG_STRING_FN_DECLARE(sDebug_), PartSize eParentPartSize )
#else
Void TEncCu::xCompressCU( TComDataCU*& rpcBestCU, TComDataCU*& rpcTempCU, const UInt uiDepth )
#endif
{TComPic* pcPic = rpcBestCU->getPic();DEBUG_STRING_NEW(sDebug)const TComPPS &pps=*(rpcTempCU->getSlice()->getPPS());const TComSPS &sps=*(rpcTempCU->getSlice()->getSPS());// These are only used if getFastDeltaQp() is trueconst UInt fastDeltaQPCuMaxSize    = Clip3(sps.getMaxCUHeight()>>sps.getLog2DiffMaxMinCodingBlockSize(), sps.getMaxCUHeight(), 32u);// get Original YUV data from picture// *** 获取rpcBestCU的数据并存放到 m_ppcOrigYuv[uiDepth]地址处m_ppcOrigYuv[uiDepth]->copyFromPicYuv( pcPic->getPicYuvOrg(), rpcBestCU->getCtuRsAddr(), rpcBestCU->getZorderIdxInCtu() ); // *** m_ppcOrigYuv 存放 YUV data// variable for Cbf fast mode PU decisionBool    doNotBlockPu = true; // *** false时跳过后续模式的检测Bool    earlyDetectionSkipMode = false; // *** true时跳过后续模式及后续深度的检测const UInt uiLPelX   = rpcBestCU->getCUPelX(); // *** 上下左右边界的位置const UInt uiRPelX   = uiLPelX + rpcBestCU->getWidth(0)  - 1;const UInt uiTPelY   = rpcBestCU->getCUPelY();const UInt uiBPelY   = uiTPelY + rpcBestCU->getHeight(0) - 1;const UInt uiWidth   = rpcBestCU->getWidth(0);Int iBaseQP = xComputeQP( rpcBestCU, uiDepth );Int iMinQP;Int iMaxQP;Bool isAddLowestQP = false;const UInt numberValidComponents = rpcBestCU->getPic()->getNumberValidComponents();if( uiDepth <= pps.getMaxCuDQPDepth() ){Int idQP = m_pcEncCfg->getMaxDeltaQP();iMinQP = Clip3( -sps.getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, iBaseQP-idQP );iMaxQP = Clip3( -sps.getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, iBaseQP+idQP );}else{iMinQP = rpcTempCU->getQP(0);iMaxQP = rpcTempCU->getQP(0);}if ( m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() ) // 若使能,则QP固定?{if ( uiDepth <= pps.getMaxCuDQPDepth() ){// keep using the same m_QP_LUMA_OFFSET in the same CTUm_lumaQPOffset = calculateLumaDQP(rpcTempCU, 0, m_ppcOrigYuv[uiDepth]);}iMinQP = Clip3(-sps.getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, iBaseQP - m_lumaQPOffset);iMaxQP = iMinQP; // force encode choose the modified QO}if ( m_pcEncCfg->getUseRateCtrl() ){iMinQP = m_pcRateCtrl->getRCQP();iMaxQP = m_pcRateCtrl->getRCQP();}// transquant-bypass (TQB) processing loop variable initialisation ---const Int lowestQP = iMinQP; // *** For TQB, use this QP which is the lowest non TQB QP tested (rather than QP'=0) - that way delta QPs are smaller, *** and TQB can be tested at all CU levels.if ( (pps.getTransquantBypassEnabledFlag()) ){isAddLowestQP = true; // mark that the first iteration is to cost TQB mode.iMinQP = iMinQP - 1;  // increase loop variable range by 1, to allow testing of TQB mode along with other QPsif ( m_pcEncCfg->getCUTransquantBypassFlagForceValue() ) // *** ?{iMaxQP = iMinQP;}}TComSlice * pcSlice = rpcTempCU->getPic()->getSlice(rpcTempCU->getPic()->getCurrSliceIdx());const Bool bBoundary = !( uiRPelX < sps.getPicWidthInLumaSamples() && uiBPelY < sps.getPicHeightInLumaSamples() ); // true 表明是边界if ( !bBoundary ) // 非边界情况{for (Int iQP=iMinQP; iQP<=iMaxQP; iQP++) // ***{const Bool bIsLosslessMode = isAddLowestQP && (iQP == iMinQP); // 对应 TQB模式即无失真模式?if (bIsLosslessMode){iQP = lowestQP;}if ( m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() && uiDepth <= pps.getMaxCuDQPDepth() ){getSliceEncoder()->updateLambda(pcSlice, iQP); // *** Lambda值 随 QP值变化}m_cuChromaQpOffsetIdxPlus1 = 0;if (pcSlice->getUseChromaQpAdj()){/* Pre-estimation of chroma QP based on input block activity may be performed* here, using for example m_ppcOrigYuv[uiDepth] *//* To exercise the current code, the index used for adjustment is based on* block position*/Int lgMinCuSize = sps.getLog2MinCodingBlockSize() +std::max<Int>(0, sps.getLog2DiffMaxMinCodingBlockSize()-Int(pps.getPpsRangeExtension().getDiffCuChromaQpOffsetDepth()));m_cuChromaQpOffsetIdxPlus1 = ((uiLPelX >> lgMinCuSize) + (uiTPelY >> lgMinCuSize)) % (pps.getPpsRangeExtension().getChromaQpOffsetListLen() + 1);}rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode ); // *** 为rpcTempCU初始化预测数据(CU大小、代价、失真、编码位数等)// do inter modes, SKIP and 2Nx2Nif( rpcBestCU->getSlice()->getSliceType() != I_SLICE ) // *** I Slice不支持帧间预测模式 {// 2Nx2Nif(m_pcEncCfg->getUseEarlySkipDetection()) // *** EarlySkipDetection开启时,需要将Inter_2Nx2N与Merge模式进行比较评估{xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2Nx2N DEBUG_STRING_PASS_INTO(sDebug) ); rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );//by Competition for inter_2Nx2N}// SKIPxCheckRDCostMerge2Nx2N( rpcBestCU, rpcTempCU DEBUG_STRING_PASS_INTO(sDebug), &earlyDetectionSkipMode );//  *** 若当前CU模式rpcTempCU的代价小于rpcBestCU,则rpcBestCU会被更新为rpcTempCU,即此处是将Merge模式与2N x 2N模式进行比较评估。当getUseEarlySkipDetection为true,且Merge模式代价小于2Nx2N,且预测残差为0,此时会将Skip模式设为最优模式,即将earlyDetectionSkipMode标志位设为true。注意:只有当getUseEarlySkipDetection为true时,earlyDetectionSkipMode才可能设为truerpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );if(!m_pcEncCfg->getUseEarlySkipDetection()){// 2Nx2N, NxNxCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2Nx2N DEBUG_STRING_PASS_INTO(sDebug) );rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );if(m_pcEncCfg->getUseCbfFastMode()) // *** 需打开Cbf Fast Mode{doNotBlockPu = rpcBestCU->getQtRootCbf( 0 ) != 0; // *** 残差为0时,doNotBlockPu为false,即会跳过当前深度对后续模式的评估}}}if (bIsLosslessMode) // Restore loop variable if lossless mode was searched.{iQP = iMinQP;}}// *** earlyDetectionSkipMode 为Skip模式的标志位,true--跳过对后续模式的检测if(!earlyDetectionSkipMode) // *** earlyDetectionSkipMode 默认为 false, 即默认会执行内部代码{for (Int iQP=iMinQP; iQP<=iMaxQP; iQP++){const Bool bIsLosslessMode = isAddLowestQP && (iQP == iMinQP); // If lossless, then iQP is irrelevant for subsequent modules.if (bIsLosslessMode){iQP = lowestQP;}rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );// do inter modes, NxN, 2NxN, and Nx2Nif( rpcBestCU->getSlice()->getSliceType() != I_SLICE ){// 2Nx2N, NxNif(!( (rpcBestCU->getWidth(0)==8) && (rpcBestCU->getHeight(0)==8) )) // *** 8x8 不进行 N x N模式的评估{if( uiDepth == sps.getLog2DiffMaxMinCodingBlockSize() && doNotBlockPu) // *** 当达到最大深度时,才会进行 NxN模式的评估{xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_NxN DEBUG_STRING_PASS_INTO(sDebug)   );rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );}}if(doNotBlockPu) {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;// 从这里可以看出,每评估一个模式,都会对doNotBlockPu进行更新,若满足Cbf快速模式的条件(当前最优模式残差为0),则将其设为false,则后续模式便不再进行评估}}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)if(sps.getUseAMP() && uiDepth < sps.getLog2DiffMaxMinCodingBlockSize() ){#if AMP_ENC_SPEEDUPBool bTestAMP_Hor = false, bTestAMP_Ver = false;#if AMP_MRGBool bTestMergeAMP_Hor = false, bTestMergeAMP_Ver = false;deriveTestModeAMP (rpcBestCU, eParentPartSize, bTestAMP_Hor, bTestAMP_Ver, bTestMergeAMP_Hor, bTestMergeAMP_Ver); // *** 关键代码,该函数根据当前最优CU模式,对bTestAMP_Hor,bTestAMP_Ver,bTestMergeAMP_Hor,bTestMergeAMP_Ver四个控制位进行更新,用于控制后续评估操作是否打开
#elsederiveTestModeAMP (rpcBestCU, eParentPartSize, bTestAMP_Hor, bTestAMP_Ver);
#endif//! Do horizontal AMPif ( bTestAMP_Hor ) // true时,对水平AMP模式进行评估{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){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_MRGelse if ( bTestMergeAMP_Hor )// true时,对水平AMP模式进行Merge评估,即不进行ME运动搜索过程{if(doNotBlockPu){xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2NxnU DEBUG_STRING_PASS_INTO(sDebug), true ); // *** bUseMRG为true--只会调用xMergeEstimation函数选择最优Merge模式,不会进行运动估计rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_2NxnU ){doNotBlockPu = rpcBestCU->getQtRootCbf( 0 ) != 0;}}if(doNotBlockPu){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 AMPif ( bTestAMP_Ver )// true时,对垂直AMP模式进行评估{if(doNotBlockPu){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){xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_nRx2N DEBUG_STRING_PASS_INTO(sDebug) );rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );}}
#if AMP_MRGelse if ( bTestMergeAMP_Ver )// true时,对垂直AMP模式进行Merge评估,即不进行ME运动搜索过程{if(doNotBlockPu){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){xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_nRx2N DEBUG_STRING_PASS_INTO(sDebug), true );rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );}}
#endif#elsexCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2NxnU );rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2NxnD );rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_nLx2N );rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_nRx2N );rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );#endif}}// do normal intra modes// speedup for inter frames
#if MCTS_ENC_CHECKif ( m_pcEncCfg->getTMCTSSEITileConstraint() || (rpcBestCU->getSlice()->getSliceType() == I_SLICE) ||((!m_pcEncCfg->getDisableIntraPUsInInterSlices()) && ((rpcBestCU->getCbf(0, COMPONENT_Y) != 0) ||((rpcBestCU->getCbf(0, COMPONENT_Cb) != 0) && (numberValidComponents > COMPONENT_Cb)) ||((rpcBestCU->getCbf(0, COMPONENT_Cr) != 0) && (numberValidComponents > COMPONENT_Cr))  // avoid very complex intra if it is unlikely))){#elseif((rpcBestCU->getSlice()->getSliceType() == I_SLICE)                                        ||((!m_pcEncCfg->getDisableIntraPUsInInterSlices()) && ((rpcBestCU->getCbf( 0, COMPONENT_Y  ) != 0)                                            ||((rpcBestCU->getCbf( 0, COMPONENT_Cb ) != 0) && (numberValidComponents > COMPONENT_Cb)) ||((rpcBestCU->getCbf( 0, COMPONENT_Cr ) != 0) && (numberValidComponents > COMPONENT_Cr))  // avoid very complex intra if it is unlikely))){#endif xCheckRDCostIntra( rpcBestCU, rpcTempCU, SIZE_2Nx2N DEBUG_STRING_PASS_INTO(sDebug) ); // *** 对帧内预测模式进行评估rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );if( uiDepth == sps.getLog2DiffMaxMinCodingBlockSize() ){if( rpcTempCU->getWidth(0) > ( 1 << sps.getQuadtreeTULog2MinSize() ) ){xCheckRDCostIntra( rpcBestCU, rpcTempCU, SIZE_NxN DEBUG_STRING_PASS_INTO(sDebug)   );rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );}}}// test PCMif(sps.getUsePCM()&& rpcTempCU->getWidth(0) <= (1<<sps.getPCMLog2MaxSize())&& rpcTempCU->getWidth(0) >= (1<<sps.getPCMLog2MinSize()) ){UInt uiRawBits = getTotalBits(rpcBestCU->getWidth(0), rpcBestCU->getHeight(0), rpcBestCU->getPic()->getChromaFormat(), sps.getBitDepths().recon);UInt uiBestBits = rpcBestCU->getTotalBits();if((uiBestBits > uiRawBits) || (rpcBestCU->getTotalCost() > m_pcRdCost->calcRdCost(uiRawBits, 0))){xCheckIntraPCM (rpcBestCU, rpcTempCU);// *** 对PCM模式进行评估rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );}}if (bIsLosslessMode) // Restore loop variable if lossless mode was searched.{iQP = iMinQP;}}}if( rpcBestCU->getTotalCost()!=MAX_DOUBLE ) // *** 为rpcBestCU 编码split flag,更新率失真代价{m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[uiDepth][CI_NEXT_BEST]);m_pcEntropyCoder->resetBits();m_pcEntropyCoder->encodeSplitFlag( rpcBestCU, 0, uiDepth, true );rpcBestCU->getTotalBits() += m_pcEntropyCoder->getNumberOfWrittenBits(); // split bitsrpcBestCU->getTotalBins() += ((TEncBinCABAC *)((TEncSbac*)m_pcEntropyCoder->m_pcEntropyCoderIf)->getEncBinIf())->getBinsCoded();rpcBestCU->getTotalCost()  = m_pcRdCost->calcRdCost( rpcBestCU->getTotalBits(), rpcBestCU->getTotalDistortion() );m_pcRDGoOnSbacCoder->store(m_pppcRDSbacCoder[uiDepth][CI_NEXT_BEST]);}}// copy original YUV samples to PCM bufferif( rpcBestCU->getTotalCost()!=MAX_DOUBLE && rpcBestCU->isLosslessCoded(0) && (rpcBestCU->getIPCMFlag(0) == false)){xFillPCMBuffer(rpcBestCU, m_ppcOrigYuv[uiDepth]);}if( uiDepth == pps.getMaxCuDQPDepth() ){Int idQP = m_pcEncCfg->getMaxDeltaQP();iMinQP = Clip3( -sps.getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, iBaseQP-idQP );iMaxQP = Clip3( -sps.getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, iBaseQP+idQP );}else if( uiDepth < pps.getMaxCuDQPDepth() ){iMinQP = iBaseQP;iMaxQP = iBaseQP;}else{const Int iStartQP = rpcTempCU->getQP(0);iMinQP = iStartQP;iMaxQP = iStartQP;}if ( m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() ){iMinQP = Clip3(-sps.getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, iBaseQP - m_lumaQPOffset);iMaxQP = iMinQP;}if ( m_pcEncCfg->getUseRateCtrl() ){iMinQP = m_pcRateCtrl->getRCQP();iMaxQP = m_pcRateCtrl->getRCQP();}if ( m_pcEncCfg->getCUTransquantBypassFlagForceValue() ){iMaxQP = iMinQP; // If all TUs are forced into using transquant bypass, do not loop here.}// *** 默认配置下, bSubBranch 总为 trueconst Bool bSubBranch = bBoundary || !( m_pcEncCfg->getUseEarlyCU() && rpcBestCU->getTotalCost()!=MAX_DOUBLE && rpcBestCU->isSkipped(0) ); // 这里可以看出,当前最优CU模式为Skip时(且非边界),bSubBranch会设为false// *** bSubBranch为false,则跳过该函数体if( bSubBranch && uiDepth < sps.getLog2DiffMaxMinCodingBlockSize() && (!getFastDeltaQp() || uiWidth > fastDeltaQPCuMaxSize || bBoundary)){// 该函数体是在做四叉树划分的操作// further splitDouble splitTotalCost = 0;for (Int iQP=iMinQP; iQP<=iMaxQP; iQP++){const Bool bIsLosslessMode = false; // False at this level. Next level down may set it to true.rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );UChar       uhNextDepth         = uiDepth+1;TComDataCU* pcSubBestPartCU     = m_ppcBestCU[uhNextDepth];TComDataCU* pcSubTempPartCU     = m_ppcTempCU[uhNextDepth];DEBUG_STRING_NEW(sTempDebug)for ( UInt uiPartUnitIdx = 0; uiPartUnitIdx < 4; uiPartUnitIdx++ ){pcSubBestPartCU->initSubCU( rpcTempCU, uiPartUnitIdx, uhNextDepth, iQP );           // clear sub partition datas or init.pcSubTempPartCU->initSubCU( rpcTempCU, uiPartUnitIdx, uhNextDepth, iQP );           // clear sub partition datas or init.if( ( pcSubBestPartCU->getCUPelX() < sps.getPicWidthInLumaSamples() ) && ( pcSubBestPartCU->getCUPelY() < sps.getPicHeightInLumaSamples() ) ){if ( 0 == uiPartUnitIdx) //initialize RD with previous depth buffer{m_pppcRDSbacCoder[uhNextDepth][CI_CURR_BEST]->load(m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST]);}else{m_pppcRDSbacCoder[uhNextDepth][CI_CURR_BEST]->load(m_pppcRDSbacCoder[uhNextDepth][CI_NEXT_BEST]);}#if AMP_ENC_SPEEDUPDEBUG_STRING_NEW(sChild)if ( !(rpcBestCU->getTotalCost()!=MAX_DOUBLE && rpcBestCU->isInter(0)) ){xCompressCU( pcSubBestPartCU, pcSubTempPartCU, uhNextDepth DEBUG_STRING_PASS_INTO(sChild), NUMBER_OF_PART_SIZES );}else{xCompressCU( pcSubBestPartCU, pcSubTempPartCU, uhNextDepth DEBUG_STRING_PASS_INTO(sChild), rpcBestCU->getPartitionSize(0) ); // *** 递归调用xCompressCU 进行下一深度的搜索评估操作}DEBUG_STRING_APPEND(sTempDebug, sChild)
#elsexCompressCU( pcSubBestPartCU, pcSubTempPartCU, uhNextDepth );
#endifrpcTempCU->copyPartFrom( pcSubBestPartCU, uiPartUnitIdx, uhNextDepth );         // Keep best part data to current temporary data.xCopyYuv2Tmp( pcSubBestPartCU->getTotalNumPart()*uiPartUnitIdx, uhNextDepth );if ( m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() && pps.getMaxCuDQPDepth() >= 1 ){splitTotalCost += pcSubBestPartCU->getTotalCost();}}else{pcSubBestPartCU->copyToPic( uhNextDepth );rpcTempCU->copyPartFrom( pcSubBestPartCU, uiPartUnitIdx, uhNextDepth );}}m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[uhNextDepth][CI_NEXT_BEST]);if( !bBoundary ){m_pcEntropyCoder->resetBits();m_pcEntropyCoder->encodeSplitFlag( rpcTempCU, 0, uiDepth, true );if ( m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() && pps.getMaxCuDQPDepth() >= 1 ){Int splitBits = m_pcEntropyCoder->getNumberOfWrittenBits();Double splitBitCost = m_pcRdCost->calcRdCost( splitBits, 0 );splitTotalCost += splitBitCost;}rpcTempCU->getTotalBits() += m_pcEntropyCoder->getNumberOfWrittenBits(); // split bitsrpcTempCU->getTotalBins() += ((TEncBinCABAC *)((TEncSbac*)m_pcEntropyCoder->m_pcEntropyCoderIf)->getEncBinIf())->getBinsCoded();}if ( m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() && pps.getMaxCuDQPDepth() >= 1 ){rpcTempCU->getTotalCost() = splitTotalCost;}else{rpcTempCU->getTotalCost()  = m_pcRdCost->calcRdCost( rpcTempCU->getTotalBits(), rpcTempCU->getTotalDistortion() );}if( uiDepth == pps.getMaxCuDQPDepth() && pps.getUseDQP()){Bool hasResidual = false;for( UInt uiBlkIdx = 0; uiBlkIdx < rpcTempCU->getTotalNumPart(); uiBlkIdx ++){if( (     rpcTempCU->getCbf(uiBlkIdx, COMPONENT_Y)|| (rpcTempCU->getCbf(uiBlkIdx, COMPONENT_Cb) && (numberValidComponents > COMPONENT_Cb))|| (rpcTempCU->getCbf(uiBlkIdx, COMPONENT_Cr) && (numberValidComponents > COMPONENT_Cr)) ) ){hasResidual = true;break;}}if ( hasResidual ){m_pcEntropyCoder->resetBits();m_pcEntropyCoder->encodeQP( rpcTempCU, 0, false );rpcTempCU->getTotalBits() += m_pcEntropyCoder->getNumberOfWrittenBits(); // dQP bitsrpcTempCU->getTotalBins() += ((TEncBinCABAC *)((TEncSbac*)m_pcEntropyCoder->m_pcEntropyCoderIf)->getEncBinIf())->getBinsCoded();rpcTempCU->getTotalCost()  = m_pcRdCost->calcRdCost( rpcTempCU->getTotalBits(), rpcTempCU->getTotalDistortion() );Bool foundNonZeroCbf = false;rpcTempCU->setQPSubCUs( rpcTempCU->getRefQP( 0 ), 0, uiDepth, foundNonZeroCbf );assert( foundNonZeroCbf );}else{rpcTempCU->setQPSubParts( rpcTempCU->getRefQP( 0 ), 0, uiDepth ); // set QP to default QP}}m_pcRDGoOnSbacCoder->store(m_pppcRDSbacCoder[uiDepth][CI_TEMP_BEST]);// If the configuration being tested exceeds the maximum number of bytes for a slice / slice-segment, then// a proper RD evaluation cannot be performed. Therefore, termination of the// slice/slice-segment must be made prior to this CTU.// This can be achieved by forcing the decision to be that of the rpcTempCU.// The exception is each slice / slice-segment must have at least one CTU.if (rpcBestCU->getTotalCost()!=MAX_DOUBLE){const Bool isEndOfSlice        =    pcSlice->getSliceMode()==FIXED_NUMBER_OF_BYTES&& ((pcSlice->getSliceBits()+rpcBestCU->getTotalBits())>pcSlice->getSliceArgument()<<3)&& rpcBestCU->getCtuRsAddr() != pcPic->getPicSym()->getCtuTsToRsAddrMap(pcSlice->getSliceCurStartCtuTsAddr())&& rpcBestCU->getCtuRsAddr() != pcPic->getPicSym()->getCtuTsToRsAddrMap(pcSlice->getSliceSegmentCurStartCtuTsAddr());const Bool isEndOfSliceSegment =    pcSlice->getSliceSegmentMode()==FIXED_NUMBER_OF_BYTES&& ((pcSlice->getSliceSegmentBits()+rpcBestCU->getTotalBits()) > pcSlice->getSliceSegmentArgument()<<3)&& rpcBestCU->getCtuRsAddr() != pcPic->getPicSym()->getCtuTsToRsAddrMap(pcSlice->getSliceSegmentCurStartCtuTsAddr());// Do not need to check slice condition for slice-segment since a slice-segment is a subset of a slice.if(isEndOfSlice||isEndOfSliceSegment){rpcBestCU->getTotalCost()=MAX_DOUBLE;}}xCheckBestMode( rpcBestCU, rpcTempCU, uiDepth DEBUG_STRING_PASS_INTO(sDebug) DEBUG_STRING_PASS_INTO(sTempDebug) DEBUG_STRING_PASS_INTO(false) ); // RD compare current larger prediction// with sub partitioned prediction.}}DEBUG_STRING_APPEND(sDebug_, sDebug);rpcBestCU->copyToPic(uiDepth);                                                     // Copy Best data to Picture for next partition prediction.xCopyYuv2Pic( rpcBestCU->getPic(), rpcBestCU->getCtuRsAddr(), rpcBestCU->getZorderIdxInCtu(), uiDepth, uiDepth );   // Copy Yuv data to picture Yuvif (bBoundary){return;}// Assert if Best prediction mode is NONE// Selected mode's RD-cost must be not MAX_DOUBLE.assert( rpcBestCU->getPartitionSize ( 0 ) != NUMBER_OF_PART_SIZES       );assert( rpcBestCU->getPredictionMode( 0 ) != NUMBER_OF_PREDICTION_MODES );assert( rpcBestCU->getTotalCost     (   ) != MAX_DOUBLE                 );
}

HEVC参考软件HM源码分析--帧间预测(1)--xCompressCU相关推荐

  1. HEVC参考软件HM源码分析--帧间预测(3)--predInterSearch

    帧间预测基本知识 对帧间预测有所了解的同学应该都知道,帧间预测操作一般都是由两个最基本也是最核心的操作组成:运动估计(ME)和运动补偿(MC). 运动估计:负责找到当前帧和参考帧之间的匹配运动矢量(M ...

  2. HEVC官方软件HM源代码简单分析-解码器TAppDecoder

    ===================================================== H.264/H.265 官方源代码分析文章: H.264官方软件JM源代码简单分析-编码器l ...

  3. 微软开源软件特征源码分析工具 Application Inspector

    微软近日开源了其内部使用的软件特征源码分析工具 Application Inspector. 现代软件开发实践通常需要基于数百个现有组件中构建应用,无论它们是由组织中的另一个团队.外部供应商还是开源社 ...

  4. 【Codecs系列】HEVC官方软件HM源代码简单分析-解码器TAppDecoder

    目录 函数调用关系图 普通内部函数 解析函数(Parser) 熵解码函数(Entropy Decoding) 解码函数(Decode) 环路滤波函数(Loop Filter) 本文记录HEVC官方参考 ...

  5. HEVC参考软件HM的使用

    文章写得很好,转载以防丢失 作者:66 (转载请务必注明出处) 学习HEVC的相关知识已经快一个月了,以前从来没有记笔记的习惯,现在自学,在理解和记忆上都比以前要迟钝许多,养成定期总结是非常有必要的. ...

  6. Android进阶——ExoPlayer源码分析之宽带预测策略的算法详解

    前言 由于国内基础设施非常优秀,在平时的开发中,很少会关注网络情况,很容易忽略弱网情况下的网络状况,如果项目属于国外App,则需要考虑到当前的基础设施和网络情况,特别是播放视频的时候,需要通过动态调整 ...

  7. Linux下存储多路径软件MultiPath源码分析

    2019独角兽企业重金招聘Python工程师标准>>> 全局概览 测试环境为CentOS 7 X64 从RPM获取源码 $ cd ~/rpmbuild/ $ yumdownloade ...

  8. HM代码阅读1: 帧间预测函数Void TEncSearch::predInterSearch()

    AMVP理论知识简单回顾(含GPB) MVP主要是为了给当前PU提供一个运动矢量的预测,可加快ME的计算速度以及提升准确性.并且在后续编码中也只用编码MVD,减少了传输bit数. HM中获取每个参考图 ...

  9. Ciclop开源3D扫描仪软件---Horus源码分析之Image_capture.py

    *                                                 联系方式:  *                                           ...

最新文章

  1. Effective Java - Item 1: Consider static factory methods instead of constructors
  2. linux 命令 跳过yes,Linux命令之yes
  3. python 信号量,Event, 定时器
  4. Windows内核新手上路3——挂钩KeUserModeCallBack
  5. C# HttpHelper帮助类,真正的Httprequest请求时无视编码,无视证书,无视Cookie,网页抓取...
  6. 【Linux】【服务器】 CentOS7下安装JDK详细过程步骤
  7. JS数据分组[JSON]
  8. 用yacc编写的算术运算计算器_如何用纯机械实现乘除运算,这是个问题
  9. win10系统安装虚拟机
  10. 多媒体计算机主机系统,多媒体计算机系统的组成
  11. 苹果手机 和安卓手机调用相机和相册
  12. 是非人生 — 一个菜鸟程序员的5年职场路 第14节
  13. 完整 Python中切片说明 arr [start: end: step] (arr [-1]、arr[:-1]、arr [::-1] 等的区别)
  14. Verilog中repeat的用法
  15. 【vim小小记】vim的复制粘贴(包括系统剪贴板)
  16. 2组语法,1个函数,教你学会用Python做数据分析!
  17. 防火墙(ASA)的基本配置与远程管理
  18. 自动驾驶|马斯克推特宣布特斯拉全自动驾驶选项下月再涨1000美元
  19. 蒙哥马利算法(Montgomery Algorithm)|蒙哥马利约简、模乘、模幂
  20. 轨迹聚类之分段聚类方法总结

热门文章

  1. PYTHON,使用for循环讲述小蝌蚪找妈妈
  2. 数字工厂解决方案,先进的数字工厂怎么建立?
  3. HTML5+CSS3之隐藏文本内容鼠标悬停显示所有
  4. LED火焰灯方案-LED火焰灯方案,太阳能火把灯IC
  5. 关于 https://www.duba.com/?f=qd_sch 这个主页的问题
  6. windows 下cmake的使用
  7. 内外业协同作业的零门槛,高效率信息化外业采集神器!
  8. LaTeX各种命令及符号
  9. 乔布斯(Steve Jobs)05年在斯但福大学的演讲
  10. 产品经理的考核与评级