HEVC代码学习34:compressSlice函数
来源:互联网 发布:淘宝订单号泄漏 编辑:程序博客网 时间:2024/05/11 01:30
compressSlice,是Slice层编码的入口函数,主要完成的功能就是Slice层编码参数的初始化,其中会调用compressCtu(其中会调用xCompressCU,对CU进行划分)和encodeCtu(其中对调用xEncodeCU,对CU进行编码),这两个函数只是入口函数,十分简单,在本文第二部分进行分析。
compressSlice
主要工作流程:
1.计算Slice的起始CTU和边界CTU。
2.初始化率失真参数:bit、RD cost、失真。
3.初始化Sbac熵编码器,根据当前Slice设置参数。
4.遍历当前Slice中的CTU,对CU进行划分和编码,计算bit、RD cost、失真。其中调用了重要函数compressCtu和encodeCtu。
推荐阅读http://blog.csdn.net/nb_vol_1/article/details/51151803,不过大神使用的HM版本较早,没有引入CTU,使用的是LCU。
代码分析:
/** \param pcPic picture class *///Slice层编码Void TEncSlice::compressSlice( TComPic* pcPic, const Bool bCompressEntireSlice, const Bool bFastDeltaQP ){ // if bCompressEntireSlice is true, then the entire slice (not slice segment) is compressed, // effectively disabling the slice-segment-mode. //起始CTU序号 UInt startCtuTsAddr; //CTU边界序号 UInt boundingCtuTsAddr; //当前Slice TComSlice* const pcSlice = pcPic->getSlice(getSliceIdx()); //将Slice中当前bit置零 pcSlice->setSliceSegmentBits(0); //计算当前Slice中的起始CTU和边界CTU xDetermineStartAndBoundingCtuTsAddr ( startCtuTsAddr, boundingCtuTsAddr, pcPic ); //bCompressEntireSlice默认为false if (bCompressEntireSlice) { boundingCtuTsAddr = pcSlice->getSliceCurEndCtuTsAddr(); pcSlice->setSliceSegmentCurEndCtuTsAddr(boundingCtuTsAddr); } // initialize cost values - these are used by precompressSlice (they should be parameters). //初始化总bit、RD cost和失真 m_uiPicTotalBits = 0; m_dPicRdCost = 0; // NOTE: This is a write-only variable! m_uiPicDist = 0; //初始化熵编码器 m_pcEntropyCoder->setEntropyCoder ( m_pppcRDSbacCoder[0][CI_CURR_BEST] ); //根据当前Slice设置熵编码参数 m_pcEntropyCoder->resetEntropy ( pcSlice ); //加载熵编码器SBAC TEncBinCABAC* pRDSbacCoder = (TEncBinCABAC *) m_pppcRDSbacCoder[0][CI_CURR_BEST]->getEncBinIf(); pRDSbacCoder->setBinCountingEnableFlag( false ); pRDSbacCoder->setBinsCoded( 0 ); //bit计数 TComBitCounter tempBitCounter; //帧每行的CTU个数 const UInt frameWidthInCtus = pcPic->getPicSym()->getFrameWidthInCtus(); //快速DeltaQp默认关闭 m_pcCuEncoder->setFastDeltaQp(bFastDeltaQP); //------------------------------------------------------------------------------ // Weighted Prediction parameters estimation. //------------------------------------------------------------------------------ // calculate AC/DC values for current picture //默认关闭 if( pcSlice->getPPS()->getUseWP() || pcSlice->getPPS()->getWPBiPred() ) { xCalcACDCParamSlice(pcSlice); } const Bool bWp_explicit = (pcSlice->getSliceType()==P_SLICE && pcSlice->getPPS()->getUseWP()) || (pcSlice->getSliceType()==B_SLICE && pcSlice->getPPS()->getWPBiPred()); //bWp_explicit默认为false if ( bWp_explicit ) { //------------------------------------------------------------------------------ // Weighted Prediction implemented at Slice level. SliceMode=2 is not supported yet. //------------------------------------------------------------------------------ if ( pcSlice->getSliceMode()==FIXED_NUMBER_OF_BYTES || pcSlice->getSliceSegmentMode()==FIXED_NUMBER_OF_BYTES ) { printf("Weighted Prediction is not supported with slice mode determined by max number of bins.\n"); exit(0); } xEstimateWPParamSlice( pcSlice, m_pcCfg->getWeightedPredictionMethod() ); pcSlice->initWpScaling(pcSlice->getSPS()); // check WP on/off xCheckWPEnable( pcSlice ); }#if ADAPTIVE_QP_SELECTION //自适应QP,默认关闭 if( m_pcCfg->getUseAdaptQpSelect() && !(pcSlice->getDependentSliceSegmentFlag())) { // TODO: this won't work with dependent slices: they do not have their own QP. Check fix to mask clause execution with && !(pcSlice->getDependentSliceSegmentFlag()) m_pcTrQuant->clearSliceARLCnt(); // TODO: this looks wrong for multiple slices - the results of all but the last slice will be cleared before they are used (all slices compressed, and then all slices encoded) if(pcSlice->getSliceType()!=I_SLICE) { Int qpBase = pcSlice->getSliceQpBase(); pcSlice->setSliceQp(qpBase + m_pcTrQuant->getQpDelta(qpBase)); } }#endif // Adjust initial state if this is the start of a dependent slice. //调整初始状态 { //CTU地址 const UInt ctuRsAddr = pcPic->getPicSym()->getCtuTsToRsAddrMap( startCtuTsAddr); //当前Tile序号 const UInt currentTileIdx = pcPic->getPicSym()->getTileIdxMap(ctuRsAddr); //当前Tile const TComTile *pCurrentTile = pcPic->getPicSym()->getTComTile(currentTileIdx); //Tile中第一个CTU的地址 const UInt firstCtuRsAddrOfTile = pCurrentTile->getFirstCtuRsAddr(); //独立Slice且非第一个Tile时启用 if( pcSlice->getDependentSliceSegmentFlag() && ctuRsAddr != firstCtuRsAddrOfTile ) { // This will only occur if dependent slice-segments (m_entropyCodingSyncContextState=true) are being used. if( pCurrentTile->getTileWidthInCtus() >= 2 || !m_pcCfg->getEntropyCodingSyncEnabledFlag() ) { m_pppcRDSbacCoder[0][CI_CURR_BEST]->loadContexts( &m_lastSliceSegmentEndContextState ); } } } // for every CTU in the slice segment (may terminate sooner if there is a byte limit on the slice-segment) //遍历Slice中的每一个CTU,对CTU进行编码 for( UInt ctuTsAddr = startCtuTsAddr; ctuTsAddr < boundingCtuTsAddr; ++ctuTsAddr ) { //CTU地址 const UInt ctuRsAddr = pcPic->getPicSym()->getCtuTsToRsAddrMap(ctuTsAddr); // initialize CTU encoder //当前CTU TComDataCU* pCtu = pcPic->getCtu( ctuRsAddr ); //初始化CTU pCtu->initCtu( pcPic, ctuRsAddr ); // update CABAC state //第一个CTU的地址 const UInt firstCtuRsAddrOfTile = pcPic->getPicSym()->getTComTile(pcPic->getPicSym()->getTileIdxMap(ctuRsAddr))->getFirstCtuRsAddr(); //计算Tile的x坐标 const UInt tileXPosInCtus = firstCtuRsAddrOfTile % frameWidthInCtus; //计算CTU的x坐标 const UInt ctuXPosInCtus = ctuRsAddr % frameWidthInCtus; //如果是Tile的第一个CTU if (ctuRsAddr == firstCtuRsAddrOfTile) { //设置熵编码参数 m_pppcRDSbacCoder[0][CI_CURR_BEST]->resetEntropy(pcSlice); } else if ( ctuXPosInCtus == tileXPosInCtus && m_pcCfg->getEntropyCodingSyncEnabledFlag()) { // reset and then update contexts to the state at the end of the top-right CTU (if within current slice and tile). m_pppcRDSbacCoder[0][CI_CURR_BEST]->resetEntropy(pcSlice); // Sync if the Top-Right is available. TComDataCU *pCtuUp = pCtu->getCtuAbove(); if ( pCtuUp && ((ctuRsAddr%frameWidthInCtus+1) < frameWidthInCtus) ) { TComDataCU *pCtuTR = pcPic->getCtu( ctuRsAddr - frameWidthInCtus + 1 ); if ( pCtu->CUIsFromSameSliceAndTile(pCtuTR) ) { // Top-Right is available, we use it. m_pppcRDSbacCoder[0][CI_CURR_BEST]->loadContexts( &m_entropyCodingSyncContextState ); } } } // set go-on entropy coder (used for all trial encodings - the cu encoder and encoder search also have a copy of the same pointer) //设置熵编码器 m_pcEntropyCoder->setEntropyCoder ( m_pcRDGoOnSbacCoder ); m_pcEntropyCoder->setBitstream( &tempBitCounter ); tempBitCounter.resetBits(); m_pcRDGoOnSbacCoder->load( m_pppcRDSbacCoder[0][CI_CURR_BEST] ); // this copy is not strictly necessary here, but indicates that the GoOnSbacCoder // is reset to a known state before every decision process. ((TEncBinCABAC*)m_pcRDGoOnSbacCoder->getEncBinIf())->setBinCountingEnableFlag(true); //当前lambda Double oldLambda = m_pcRdCost->getLambda(); //码率控制时启用 if ( m_pcCfg->getUseRateCtrl() ) { Int estQP = pcSlice->getSliceQp(); Double estLambda = -1.0; Double bpp = -1.0; if ( ( pcPic->getSlice( 0 )->getSliceType() == I_SLICE && m_pcCfg->getForceIntraQP() ) || !m_pcCfg->getLCULevelRC() ) { estQP = pcSlice->getSliceQp(); } else { bpp = m_pcRateCtrl->getRCPic()->getLCUTargetBpp(pcSlice->getSliceType()); if ( pcPic->getSlice( 0 )->getSliceType() == I_SLICE) { estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambdaAndQP(bpp, pcSlice->getSliceQp(), &estQP); } else { estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambda( bpp ); estQP = m_pcRateCtrl->getRCPic()->getLCUEstQP ( estLambda, pcSlice->getSliceQp() ); } estQP = Clip3( -pcSlice->getSPS()->getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, estQP ); m_pcRdCost->setLambda(estLambda, pcSlice->getSPS()->getBitDepths());#if RDOQ_CHROMA_LAMBDA // set lambda for RDOQ const Double chromaLambda = estLambda / m_pcRdCost->getChromaWeight(); const Double lambdaArray[MAX_NUM_COMPONENT] = { estLambda, chromaLambda, chromaLambda }; m_pcTrQuant->setLambdas( lambdaArray );#else m_pcTrQuant->setLambda( estLambda );#endif } m_pcRateCtrl->setRCQP( estQP );#if ADAPTIVE_QP_SELECTION pCtu->getSlice()->setSliceQpBase( estQP );#endif } // run CTU trial encoder //CU划分 m_pcCuEncoder->compressCtu( pCtu ); // All CTU decisions have now been made. Restore entropy coder to an initial stage, ready to make a true encode, // which will result in the state of the contexts being correct. It will also count up the number of bits coded, // which is used if there is a limit of the number of bytes per slice-segment. //设置熵编码器参数 m_pcEntropyCoder->setEntropyCoder ( m_pppcRDSbacCoder[0][CI_CURR_BEST] ); m_pcEntropyCoder->setBitstream( &tempBitCounter ); pRDSbacCoder->setBinCountingEnableFlag( true ); m_pppcRDSbacCoder[0][CI_CURR_BEST]->resetBits(); pRDSbacCoder->setBinsCoded( 0 ); // encode CTU and calculate the true bit counters. //CTU编码 m_pcCuEncoder->encodeCtu( pCtu ); pRDSbacCoder->setBinCountingEnableFlag( false ); const Int numberOfWrittenBits = m_pcEntropyCoder->getNumberOfWrittenBits(); // Calculate if this CTU puts us over slice bit size. // cannot terminate if current slice/slice-segment would be 0 Ctu in size, const UInt validEndOfSliceCtuTsAddr = ctuTsAddr + (ctuTsAddr == startCtuTsAddr ? 1 : 0); // Set slice end parameter //设置Slice结束参数 if(pcSlice->getSliceMode()==FIXED_NUMBER_OF_BYTES && pcSlice->getSliceBits()+numberOfWrittenBits > (pcSlice->getSliceArgument()<<3)) { pcSlice->setSliceSegmentCurEndCtuTsAddr(validEndOfSliceCtuTsAddr); pcSlice->setSliceCurEndCtuTsAddr(validEndOfSliceCtuTsAddr); boundingCtuTsAddr=validEndOfSliceCtuTsAddr; } else if((!bCompressEntireSlice) && pcSlice->getSliceSegmentMode()==FIXED_NUMBER_OF_BYTES && pcSlice->getSliceSegmentBits()+numberOfWrittenBits > (pcSlice->getSliceSegmentArgument()<<3)) { pcSlice->setSliceSegmentCurEndCtuTsAddr(validEndOfSliceCtuTsAddr); boundingCtuTsAddr=validEndOfSliceCtuTsAddr; } //如果当前CTU超过了边界CTU,跳出 if (boundingCtuTsAddr <= ctuTsAddr) { break; } //设置bit数 pcSlice->setSliceBits( (UInt)(pcSlice->getSliceBits() + numberOfWrittenBits) ); pcSlice->setSliceSegmentBits(pcSlice->getSliceSegmentBits()+numberOfWrittenBits); // Store probabilities of second CTU in line into buffer - used only if wavefront-parallel-processing is enabled. //默认关闭 if ( ctuXPosInCtus == tileXPosInCtus+1 && m_pcCfg->getEntropyCodingSyncEnabledFlag()) { m_entropyCodingSyncContextState.loadContexts(m_pppcRDSbacCoder[0][CI_CURR_BEST]); } //码率控制时启用 if ( m_pcCfg->getUseRateCtrl() ) { Int actualQP = g_RCInvalidQPValue; Double actualLambda = m_pcRdCost->getLambda(); Int actualBits = pCtu->getTotalBits(); Int numberOfEffectivePixels = 0; for ( Int idx = 0; idx < pcPic->getNumPartitionsInCtu(); idx++ ) { if ( pCtu->getPredictionMode( idx ) != NUMBER_OF_PREDICTION_MODES && ( !pCtu->isSkipped( idx ) ) ) { numberOfEffectivePixels = numberOfEffectivePixels + 16; break; } } if ( numberOfEffectivePixels == 0 ) { actualQP = g_RCInvalidQPValue; } else { actualQP = pCtu->getQP( 0 ); } m_pcRdCost->setLambda(oldLambda, pcSlice->getSPS()->getBitDepths()); m_pcRateCtrl->getRCPic()->updateAfterCTU( m_pcRateCtrl->getRCPic()->getLCUCoded(), actualBits, actualQP, actualLambda, pCtu->getSlice()->getSliceType() == I_SLICE ? 0 : m_pcCfg->getLCULevelRC() ); } //计算总的bit、RD cost、失真 m_uiPicTotalBits += pCtu->getTotalBits(); m_dPicRdCost += pCtu->getTotalCost(); m_uiPicDist += pCtu->getTotalDistortion(); } // store context state at the end of this slice-segment, in case the next slice is a dependent slice and continues using the CABAC contexts. if( pcSlice->getPPS()->getDependentSliceSegmentsEnabledFlag() ) { m_lastSliceSegmentEndContextState.loadContexts( m_pppcRDSbacCoder[0][CI_CURR_BEST] );//ctx end of dep.slice } // stop use of temporary bit counter object. m_pppcRDSbacCoder[0][CI_CURR_BEST]->setBitstream(NULL); m_pcRDGoOnSbacCoder->setBitstream(NULL); // stop use of tempBitCounter. // TODO: optimise cabac_init during compress slice to improve multi-slice operation //if (pcSlice->getPPS()->getCabacInitPresentFlag() && !pcSlice->getPPS()->getDependentSliceSegmentsEnabledFlag()) //{ // m_encCABACTableIdx = m_pcEntropyCoder->determineCabacInitIdx(); //} //else //{ // m_encCABACTableIdx = pcSlice->getSliceType(); //}}
compressCtu和encodeCtu
compressCtu是块划分的入口函数,主要包含两部分:初始化CTU和调用xCompressCU进行块划分。xCompressCU是非常重要的函数,详细介绍见:http://blog.csdn.net/lin453701006/article/details/72401400
代码分析如下:
/** \param pCtu pointer of CU data class */Void TEncCu::compressCtu( TComDataCU* pCtu ){ // initialize CU data //将最优CU置为当前CTU m_ppcBestCU[0]->initCtu( pCtu->getPic(), pCtu->getCtuRsAddr() ); //将当前CU置为当前CTU m_ppcTempCU[0]->initCtu( pCtu->getPic(), pCtu->getCtuRsAddr() ); // analysis of CU DEBUG_STRING_NEW(sDebug) //递归进行块划分 xCompressCU( m_ppcBestCU[0], m_ppcTempCU[0], 0 DEBUG_STRING_PASS_INTO(sDebug) ); DEBUG_STRING_OUTPUT(std::cout, sDebug)#if ADAPTIVE_QP_SELECTION if( m_pcEncCfg->getUseAdaptQpSelect() ) { if(pCtu->getSlice()->getSliceType()!=I_SLICE) //IIII { xCtuCollectARLStats( pCtu ); } }#endif}
encodeCtu是CTU编码的入口函数,主要包含两部分:根据情况设置QP相关Flag和调用xEncodeCU进行CU编码。xEncodeCU详细分析见http://blog.csdn.net/lin453701006/article/details/78688889。
代码分析如下:
/** \param pCtu pointer of CU data class */Void TEncCu::encodeCtu ( TComDataCU* pCtu ){ //当开启DQP时,置flag if ( pCtu->getSlice()->getPPS()->getUseDQP() ) { setdQPFlag(true); } //当开启色度QP自适应时,置flag if ( pCtu->getSlice()->getUseChromaQpAdj() ) { setCodeChromaQpAdjFlag(true); } // Encode CU data //CU编码 xEncodeCU( pCtu, 0, 0 );}
- HEVC代码学习34:compressSlice函数
- HEVC代码追踪(五):compressSlice
- HEVC代码学习2:TAppEncTop::encode函数
- HEVC代码学习3:TEncTop::encode函数
- HEVC代码学习7:xPatternSearchFracDIF函数
- HEVC代码学习8:xMotionEstimation函数
- HEVC代码学习9:getInterMergeCandidates函数
- HEVC代码学习11:xCompressCU函数
- HEVC代码学习12:xCheckRDCostInter函数
- HEVC代码学习13:predInterSearch函数
- HEVC代码学习14:motionCompensation函数
- HEVC代码学习15:AMVP相关函数
- HEVC代码学习20:xPatternSearchFast函数
- HEVC代码学习21:xTZSearch函数
- HEVC代码学习22:xTZSearchHelp函数
- HEVC代码学习23:xTZ8PointDiamondSearch函数
- HEVC代码学习24:encodeResAndCalcRdInterCU函数
- HEVC代码学习25:xDecompressCU函数
- 【Solaris x86安装VIM 】
- 验证算法(递归的折半查找)
- 对象概述
- 转载 linux端口重用
- 第十二周项目三
- HEVC代码学习34:compressSlice函数
- 'localtime': This function or variable may be unsafe. Consider using localtime_s instead. To disable
- 设计模式
- 第13周 项目一 验证折半查找算法
- 沙箱技术
- Android-ABIFilter-Device supports x86,but APK only supports armeabi-v7a,armeabi,x86_64
- 关于底部导航栏的跳转问题
- 移动端事件及事件应用
- 关于堆和栈的那些事