HM的码率控制提案主要参考如下三篇:K0103,M0036,M0257。本文及后续文章将基于HM12.0进行讨论,且首先仅讨论K0103对应的代码,之后再陆续补充M0036,M0257对应的代码分析,这么做可能会使得剧情不会显得那么地跳跃,分析起来能够更好地被接受。
按照我的个人习惯,还是先分析HM中码率控制部分(以后简称RC)的总体框架吧。
跟RC有关的头文件和源文件为工程TLibEncoder中的TEncRateCtrl.h和TEncRateCtrl.cpp,其余的地方都是调用这两个文件中定义的函数或者变量。
在最顶层,TEncTop这个类定义了成员变量TEncRateCtrl m_cRateCtrl,类TEncGOP,TEncSlice以及TEncCu也分别定义了成员变量TEncRateCtrl *m_pcRateCtrl,但是请注意,这三个类的m_pcRateCtrl实际上都是指向TEncTop这个类的m_cRateCtrl,即它们本质上是同一个。这个从这三个类的成员函数init中对各成员变量的初始化可以看出来。
(1)初始化:
首先,在TEncTop::create()中,对整个序列都要用到的相关参数进行初始化
#if RATE_CONTROL_LAMBDA_DOMAIN if ( m_RCEnableRateControl ) { m_cRateCtrl.init( m_framesToBeEncoded, m_RCTargetBitrate, m_iFrameRate, m_iGOPSize, m_iSourceWidth, m_iSourceHeight, g_uiMaxCUWidth, g_uiMaxCUHeight, m_RCKeepHierarchicalBit, m_RCUseLCUSeparateModel, m_GOPList ); } #else m_cRateCtrl.create(getIntraPeriod(), getGOPSize(), getFrameRate(), getTargetBitrate(), getQP(), getNumLCUInUnit(), getSourceWidth(), getSourceHeight(), g_uiMaxCUWidth, g_uiMaxCUHeight); #endif
其次,在TEncTop::encode()中,对整个GOP需要用到的相关参数进行初始化
#if RATE_CONTROL_LAMBDA_DOMAIN if ( m_RCEnableRateControl ) { m_cRateCtrl.initRCGOP( m_iNumPicRcvd ); } #endif
接着,在TEncGOP::compressGOP()中,对每一幅picture需要用到的相关参数进行初始化
#if RATE_CONTROL_LAMBDA_DOMAIN Double lambda = 0.0; Int actualHeadBits = 0; Int actualTotalBits = 0; Int estimatedBits = 0; Int tmpBitsBeforeWriting = 0; if ( m_pcCfg->getUseRateCtrl() ) { Int frameLevel = m_pcRateCtrl->getRCSeq()->getGOPID2Level( iGOPid ); if ( pcPic->getSlice(0)->getSliceType() == I_SLICE ) { frameLevel = 0; } m_pcRateCtrl->initRCPic( frameLevel ); //!< picture level 初始化 estimatedBits = m_pcRateCtrl->getRCPic()->getTargetBits(); Int sliceQP = m_pcCfg->getInitialQP(); if ( ( pcSlice->getPOC() == 0 && m_pcCfg->getInitialQP() > 0 ) || ( frameLevel == 0 && m_pcCfg->getForceIntraQP() ) ) // QP is specified { Int NumberBFrames = ( m_pcCfg->getGOPSize() - 1 ); Double dLambda_scale = 1.0 - Clip3( 0.0, 0.5, 0.05*(Double)NumberBFrames ); Double dQPFactor = 0.57*dLambda_scale; Int SHIFT_QP = 12; Int bitdepth_luma_qp_scale = 0; Double qp_temp = (Double) sliceQP + bitdepth_luma_qp_scale - SHIFT_QP; lambda = dQPFactor*pow( 2.0, qp_temp/3.0 ); } else if ( frameLevel == 0 ) // intra case, but use the model { #if RATE_CONTROL_INTRA m_pcSliceEncoder->calCostSliceI(pcPic); #endif if ( m_pcCfg->getIntraPeriod() != 1 ) // do not refine allocated bits for all intra case { Int bits = m_pcRateCtrl->getRCSeq()->getLeftAverageBits(); #if RATE_CONTROL_INTRA bits = m_pcRateCtrl->getRCPic()->getRefineBitsForIntra( bits ); #else bits = m_pcRateCtrl->getRCSeq()->getRefineBitsForIntra( bits ); #endif if ( bits < 200 ) { bits = 200; } m_pcRateCtrl->getRCPic()->setTargetBits( bits ); } list<TEncRCPic*> listPreviousPicture = m_pcRateCtrl->getPicList(); #if RATE_CONTROL_INTRA m_pcRateCtrl->getRCPic()->getLCUInitTargetBits(); lambda = m_pcRateCtrl->getRCPic()->estimatePicLambda( listPreviousPicture, pcSlice->getSliceType()); #else lambda = m_pcRateCtrl->getRCPic()->estimatePicLambda( listPreviousPicture ); #endif sliceQP = m_pcRateCtrl->getRCPic()->estimatePicQP( lambda, listPreviousPicture ); } else // normal case { list<TEncRCPic*> listPreviousPicture = m_pcRateCtrl->getPicList(); #if RATE_CONTROL_INTRA lambda = m_pcRateCtrl->getRCPic()->estimatePicLambda( listPreviousPicture, pcSlice->getSliceType()); #else lambda = m_pcRateCtrl->getRCPic()->estimatePicLambda( listPreviousPicture ); #endif sliceQP = m_pcRateCtrl->getRCPic()->estimatePicQP( lambda, listPreviousPicture ); } sliceQP = Clip3( -pcSlice->getSPS()->getQpBDOffsetY(), MAX_QP, sliceQP ); m_pcRateCtrl->getRCPic()->setPicEstQP( sliceQP ); m_pcSliceEncoder->resetQP( pcPic, sliceQP, lambda ); } #endif
最后,在TEncSlice::compressSlice()中,对每一个LCU需要用到的相关参数进行初始化:
#if RATE_CONTROL_LAMBDA_DOMAIN Double oldLambda = m_pcRdCost->getLambda(); if ( m_pcCfg->getUseRateCtrl() ) { Int estQP = pcSlice->getSliceQp(); Double estLambda = -1.0; Double bpp = -1.0; #if M0036_RC_IMPROVEMENT if ( ( rpcPic->getSlice( 0 )->getSliceType() == I_SLICE && m_pcCfg->getForceIntraQP() ) || !m_pcCfg->getLCULevelRC() ) #else if ( rpcPic->getSlice( 0 )->getSliceType() == I_SLICE || !m_pcCfg->getLCULevelRC() ) #endif { estQP = pcSlice->getSliceQp(); } else { #if RATE_CONTROL_INTRA bpp = m_pcRateCtrl->getRCPic()->getLCUTargetBpp(pcSlice->getSliceType()); if ( rpcPic->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() ); } #else bpp = m_pcRateCtrl->getRCPic()->getLCUTargetBpp(); estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambda( bpp ); estQP = m_pcRateCtrl->getRCPic()->getLCUEstQP ( estLambda, pcSlice->getSliceQp() ); #endif estQP = Clip3( -pcSlice->getSPS()->getQpBDOffsetY(), MAX_QP, estQP ); m_pcRdCost->setLambda(estLambda); #if M0036_RC_IMPROVEMENT #if RDOQ_CHROMA_LAMBDA // set lambda for RDOQ Double weight=m_pcRdCost->getChromaWeight(); m_pcTrQuant->setLambda( estLambda, estLambda / weight ); #else m_pcTrQuant->setLambda( estLambda ); #endif #endif } m_pcRateCtrl->setRCQP( estQP ); pcCU->getSlice()->setSliceQpBase( estQP ); } #endif
(2)参数值更新:
首先,在TEncSlice::compressSlice中,每编码完一个LCU,进行一次更新:
#if TICKET_1090_FIX #if RATE_CONTROL_LAMBDA_DOMAIN if ( m_pcCfg->getUseRateCtrl() ) { #if !M0036_RC_IMPROVEMENT UInt SAD = m_pcCuEncoder->getLCUPredictionSAD(); Int height = min( pcSlice->getSPS()->getMaxCUHeight(),pcSlice->getSPS()->getPicHeightInLumaSamples() - uiCUAddr / rpcPic->getFrameWidthInCU() * pcSlice->getSPS()->getMaxCUHeight() ); Int width = min( pcSlice->getSPS()->getMaxCUWidth(),pcSlice->getSPS()->getPicWidthInLumaSamples() - uiCUAddr % rpcPic->getFrameWidthInCU() * pcSlice->getSPS()->getMaxCUWidth() ); Double MAD = (Double)SAD / (Double)(height * width); MAD = MAD * MAD; ( m_pcRateCtrl->getRCPic()->getLCU(uiCUAddr) ).m_MAD = MAD; //!< 注意:此处的MAD已经进行过K0103中公式的两个处理了。 #endif Int actualQP = g_RCInvalidQPValue; Double actualLambda = m_pcRdCost->getLambda(); Int actualBits = pcCU->getTotalBits(); Int numberOfEffectivePixels = 0; for ( Int idx = 0; idx < rpcPic->getNumPartInCU(); idx++ ) { if ( pcCU->getPredictionMode( idx ) != MODE_NONE && ( !pcCU->isSkipped( idx ) ) ) //!< 不考虑skip模式 { numberOfEffectivePixels = numberOfEffectivePixels + 16; break; } } if ( numberOfEffectivePixels == 0 ) { actualQP = g_RCInvalidQPValue; } else { actualQP = pcCU->getQP( 0 ); } m_pcRdCost->setLambda(oldLambda); #if RATE_CONTROL_INTRA m_pcRateCtrl->getRCPic()->updateAfterLCU( m_pcRateCtrl->getRCPic()->getLCUCoded(), actualBits, actualQP, actualLambda, pcCU->getSlice()->getSliceType() == I_SLICE ? 0 : m_pcCfg->getLCULevelRC() ); #else m_pcRateCtrl->getRCPic()->updateAfterLCU( m_pcRateCtrl->getRCPic()->getLCUCoded(), actualBits, actualQP, actualLambda, m_pcCfg->getLCULevelRC() ); #endif } #endif #endif
接着,在TEncGOP::compressGOP中,一个slice编码完后,进行一次更新:
#if RATE_CONTROL_LAMBDA_DOMAIN if ( m_pcCfg->getUseRateCtrl() ) { #if !M0036_RC_IMPROVEMENT Double effectivePercentage = m_pcRateCtrl->getRCPic()->getEffectivePercentage(); #endif Double avgQP = m_pcRateCtrl->getRCPic()->calAverageQP(); //!< arithmetic mean value for QP Double avgLambda = m_pcRateCtrl->getRCPic()->calAverageLambda(); //!< geometric mean value for lamda if ( avgLambda < 0.0 ) { avgLambda = lambda; } #if M0036_RC_IMPROVEMENT #if RATE_CONTROL_INTRA m_pcRateCtrl->getRCPic()->updateAfterPicture( actualHeadBits, actualTotalBits, avgQP, avgLambda, pcSlice->getSliceType()); #else m_pcRateCtrl->getRCPic()->updateAfterPicture( actualHeadBits, actualTotalBits, avgQP, avgLambda ); #endif #else m_pcRateCtrl->getRCPic()->updateAfterPicture( actualHeadBits, actualTotalBits, avgQP, avgLambda, effectivePercentage ); #endif m_pcRateCtrl->getRCPic()->addToPictureLsit( m_pcRateCtrl->getPicList() ); m_pcRateCtrl->getRCSeq()->updateAfterPic( actualTotalBits ); if ( pcSlice->getSliceType() != I_SLICE ) { m_pcRateCtrl->getRCGOP()->updateAfterPicture( actualTotalBits ); } else // for intra picture, the estimated bits are used to update the current status in the GOP { m_pcRateCtrl->getRCGOP()->updateAfterPicture( estimatedBits ); } } #else if(m_pcCfg->getUseRateCtrl()) { UInt frameBits = m_vRVM_RP[m_vRVM_RP.size()-1]; m_pcRateCtrl->updataRCFrameStatus((Int)frameBits, pcSlice->getSliceType()); } #endif
最后,在TEncTop::encode()中,对当前GOP的码控变量进行了销毁。
至此,总体框架大体分析完毕,最后,简单地提一下TEncRateCtrl.h中定义的类的关系:TEncRateCtrl属于RC顶层的控制,负责整个RC的流程管理;TEncRCSeq负责序列级的RC管理;TEncRCGOP负责GOP级的RC管理;TEncRCPic负责picture级的RC管理;TRCLCU负责LCU级的RC管理。