HEVC码率控制浅析——HM代码阅读之一

来源:互联网 发布:淘宝技术 编辑:程序博客网 时间:2024/05/16 15:13

注:本篇博客大部分内容转载于下面的链接,转载过来之后,我根据原文对博客部分内容做了更改和批注。博客原文地址如下:

http://blog.csdn.net/hevc_cjl/article/details/10982699

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中对各成员变量的初始化可以看出来。

在主函数中调用:

int main(int argc, char* argv[]){  TAppEncTop  cTAppEncTop; cTAppEncTop.create();  // call encoding function  cTAppEncTop.encode();}

Void TAppEncTop::encode(){   xCreateLib();//也是TAppEncTop的成员函数   m_cTEncTop.encode();//为TEncTop的成员函数}
Void TAppEncTop::xCreateLib(){   m_cTEncTop.create();//m_cTEncTop 为TEncTop类型的成员变量}



到此,才开始正式调用HEVC中码率控制的函数

(1)初始化:

首先,在TEncTop::create()中,对整个序列都要用到的相关参数进行初始化

[cpp] view plaincopy
  1. #if RATE_CONTROL_LAMBDA_DOMAIN  
  2.   if ( m_RCEnableRateControl )  
  3.   {  
  4.     m_cRateCtrl.init( m_framesToBeEncoded, m_RCTargetBitrate, m_iFrameRate, m_iGOPSize, m_iSourceWidth, m_iSourceHeight,  
  5.                       g_uiMaxCUWidth, g_uiMaxCUHeight, m_RCKeepHierarchicalBit, m_RCUseLCUSeparateModel, m_GOPList );  
  6.   }  
  7. #else  
  8.   m_cRateCtrl.create(getIntraPeriod(), getGOPSize(), getFrameRate(), getTargetBitrate(), getQP(), getNumLCUInUnit(), getSourceWidth(), getSourceHeight(), g_uiMaxCUWidth, g_uiMaxCUHeight);  
  9. #endif  


其次,在TEncTop::encode()中,对整个GOP需要用到的相关参数进行初始化

[cpp] view plaincopy
  1. #if RATE_CONTROL_LAMBDA_DOMAIN  
  2.   if ( m_RCEnableRateControl )  
  3.   {  
  4.     m_cRateCtrl.initRCGOP( m_iNumPicRcvd );  
  5.   }  
  6.  m_cGOPEncoder.compressGOP(m_iPOCLast, m_iNumPicRcvd, m_cListPic, rcListPicYuvRecOut, accessUnitsOut, false, false);
  7. #endif  

 

接着,在TEncGOP::compressGOP()中,对每一幅picture需要用到的相关参数进行初始化

[cpp] view plaincopy
  1. #if RATE_CONTROL_LAMBDA_DOMAIN  
  2.     Double lambda            = 0.0;  
  3.     Int actualHeadBits       = 0;  
  4.     Int actualTotalBits      = 0;  
  5.     Int estimatedBits        = 0;  
  6.     Int tmpBitsBeforeWriting = 0;  
  7.     if ( m_pcCfg->getUseRateCtrl() )  
  8.     {  
  9.       Int frameLevel = m_pcRateCtrl->getRCSeq()->getGOPID2Level( iGOPid );  
  10.       if ( pcPic->getSlice(0)->getSliceType() == I_SLICE )  
  11.       {  
  12.         frameLevel = 0;  
  13.       }  
  14.       m_pcRateCtrl->initRCPic( frameLevel ); //!< picture level 初始化  
  15.       estimatedBits = m_pcRateCtrl->getRCPic()->getTargetBits();  
  16.         
  17.       Int sliceQP = m_pcCfg->getInitialQP();  
  18.       if ( ( pcSlice->getPOC() == 0 && m_pcCfg->getInitialQP() > 0 ) || ( frameLevel == 0 && m_pcCfg->getForceIntraQP() ) ) // QP is specified  
  19.       {  
  20.         Int    NumberBFrames = ( m_pcCfg->getGOPSize() - 1 );  
  21.         Double dLambda_scale = 1.0 - Clip3( 0.0, 0.5, 0.05*(Double)NumberBFrames );  
  22.         Double dQPFactor     = 0.57*dLambda_scale;  
  23.         Int    SHIFT_QP      = 12;  
  24.         Int    bitdepth_luma_qp_scale = 0;  
  25.         Double qp_temp = (Double) sliceQP + bitdepth_luma_qp_scale - SHIFT_QP;  
  26.         lambda = dQPFactor*pow( 2.0, qp_temp/3.0 );  
  27.       }  
  28.       else if ( frameLevel == 0 )   // intra case, but use the model  
  29.       {  
  30. #if RATE_CONTROL_INTRA  
  31.         m_pcSliceEncoder->calCostSliceI(pcPic);  
  32. #endif  
  33.         if ( m_pcCfg->getIntraPeriod() != 1 )   // do not refine allocated bits for all intra case  
  34.         {  
  35.           Int bits = m_pcRateCtrl->getRCSeq()->getLeftAverageBits();  
  36. #if RATE_CONTROL_INTRA  
  37.           bits = m_pcRateCtrl->getRCPic()->getRefineBitsForIntra( bits );  
  38. #else  
  39.           bits = m_pcRateCtrl->getRCSeq()->getRefineBitsForIntra( bits );  
  40. #endif  
  41.           if ( bits < 200 )  
  42.           {  
  43.             bits = 200;  
  44.           }  
  45.           m_pcRateCtrl->getRCPic()->setTargetBits( bits );  
  46.         }  
  47.           
  48.         list<TEncRCPic*> listPreviousPicture = m_pcRateCtrl->getPicList();  
  49. #if RATE_CONTROL_INTRA  
  50.         m_pcRateCtrl->getRCPic()->getLCUInitTargetBits();  
  51.         lambda  = m_pcRateCtrl->getRCPic()->estimatePicLambda( listPreviousPicture, pcSlice->getSliceType());  
  52. #else  
  53.         lambda  = m_pcRateCtrl->getRCPic()->estimatePicLambda( listPreviousPicture );  
  54. #endif  
  55.         sliceQP = m_pcRateCtrl->getRCPic()->estimatePicQP( lambda, listPreviousPicture );  
  56.       }  
  57.       else    // normal case  
  58.       {  
  59.         list<TEncRCPic*> listPreviousPicture = m_pcRateCtrl->getPicList();  
  60. #if RATE_CONTROL_INTRA  
  61.         lambda  = m_pcRateCtrl->getRCPic()->estimatePicLambda( listPreviousPicture, pcSlice->getSliceType());  
  62. #else  
  63.         lambda  = m_pcRateCtrl->getRCPic()->estimatePicLambda( listPreviousPicture );  
  64. #endif  
  65.         sliceQP = m_pcRateCtrl->getRCPic()->estimatePicQP( lambda, listPreviousPicture );  
  66.       }  
  67.         
  68.       sliceQP = Clip3( -pcSlice->getSPS()->getQpBDOffsetY(), MAX_QP, sliceQP );  
  69.       m_pcRateCtrl->getRCPic()->setPicEstQP( sliceQP );  
  70.         
  71.       m_pcSliceEncoder->resetQP( pcPic, sliceQP, lambda );  
  72.     }  
  73. #endif  
  74.       m_pcSliceEncoder->precompressSlice( pcPic );
          m_pcSliceEncoder->compressSlice   ( pcPic ); //并没有紧跟着GOP的初始化,但是也是在当前函数中




最后,在TEncSlice::compressSlice()中,对每一个LCU需要用到的相关参数进行初始化:

[cpp] view plaincopy
  1. #if RATE_CONTROL_LAMBDA_DOMAIN  
  2.       Double oldLambda = m_pcRdCost->getLambda();  
  3.       if ( m_pcCfg->getUseRateCtrl() )  
  4.       {  
  5.         Int estQP        = pcSlice->getSliceQp();  
  6.         Double estLambda = -1.0;  
  7.         Double bpp       = -1.0;  
  8.   
  9. #if M0036_RC_IMPROVEMENT  
  10.         if ( ( rpcPic->getSlice( 0 )->getSliceType() == I_SLICE && m_pcCfg->getForceIntraQP() ) || !m_pcCfg->getLCULevelRC() )  
  11. #else  
  12.         if ( rpcPic->getSlice( 0 )->getSliceType() == I_SLICE || !m_pcCfg->getLCULevelRC() )  
  13. #endif  
  14.         {  
  15.           estQP = pcSlice->getSliceQp();  
  16.         }  
  17.         else  
  18.         {  
  19. #if RATE_CONTROL_INTRA  
  20.           bpp = m_pcRateCtrl->getRCPic()->getLCUTargetBpp(pcSlice->getSliceType());  
  21.           if ( rpcPic->getSlice( 0 )->getSliceType() == I_SLICE)  
  22.           {  
  23.             estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambdaAndQP(bpp, pcSlice->getSliceQp(), &estQP);  
  24.           }  
  25.           else  
  26.           {  
  27.             estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambda( bpp );  
  28.             estQP     = m_pcRateCtrl->getRCPic()->getLCUEstQP    ( estLambda, pcSlice->getSliceQp() );  
  29.           }  
  30. #else  
  31.           bpp       = m_pcRateCtrl->getRCPic()->getLCUTargetBpp();  
  32.           estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambda( bpp );  
  33.           estQP     = m_pcRateCtrl->getRCPic()->getLCUEstQP    ( estLambda, pcSlice->getSliceQp() );  
  34. #endif  
  35.   
  36.           estQP     = Clip3( -pcSlice->getSPS()->getQpBDOffsetY(), MAX_QP, estQP );  
  37.   
  38.           m_pcRdCost->setLambda(estLambda);  
  39. #if M0036_RC_IMPROVEMENT  
  40. #if RDOQ_CHROMA_LAMBDA  
  41.           // set lambda for RDOQ  
  42.           Double weight=m_pcRdCost->getChromaWeight();  
  43.           m_pcTrQuant->setLambda( estLambda, estLambda / weight );  
  44. #else  
  45.           m_pcTrQuant->setLambda( estLambda );  
  46. #endif  
  47. #endif  
  48.         }  
  49.   
  50.         m_pcRateCtrl->setRCQP( estQP );  
  51.         pcCU->getSlice()->setSliceQpBase( estQP );  
  52.       }  
  53. #endif  


(2)参数值更新:

首先,在TEncSlice::compressSlice中,每编码完一个LCU,进行一次更新:

[cpp] view plaincopy
  1. #if TICKET_1090_FIX  
  2. #if RATE_CONTROL_LAMBDA_DOMAIN  
  3.       if ( m_pcCfg->getUseRateCtrl() )  
  4.       {  
  5. #if !M0036_RC_IMPROVEMENT  
  6.         UInt SAD    = m_pcCuEncoder->getLCUPredictionSAD();  
  7.         Int height  = min( pcSlice->getSPS()->getMaxCUHeight(),pcSlice->getSPS()->getPicHeightInLumaSamples() - uiCUAddr / rpcPic->getFrameWidthInCU() * pcSlice->getSPS()->getMaxCUHeight() );  
  8.         Int width   = min( pcSlice->getSPS()->getMaxCUWidth(),pcSlice->getSPS()->getPicWidthInLumaSamples() - uiCUAddr % rpcPic->getFrameWidthInCU() * pcSlice->getSPS()->getMaxCUWidth() );  
  9.         Double MAD = (Double)SAD / (Double)(height * width);  
  10.         MAD = MAD * MAD;  
  11.         ( m_pcRateCtrl->getRCPic()->getLCU(uiCUAddr) ).m_MAD = MAD; //!< 注意:此处的MAD已经进行过K0103中公式的两个处理了。  
  12. #endif  
  13.   
  14.         Int actualQP        = g_RCInvalidQPValue;  
  15.         Double actualLambda = m_pcRdCost->getLambda();  
  16.         Int actualBits      = pcCU->getTotalBits();  
  17.         Int numberOfEffectivePixels    = 0;  
  18.         for ( Int idx = 0; idx < rpcPic->getNumPartInCU(); idx++ )  
  19.         {  
  20.           if ( pcCU->getPredictionMode( idx ) != MODE_NONE && ( !pcCU->isSkipped( idx ) ) ) //!< 不考虑skip模式  
  21.           {  
  22.             numberOfEffectivePixels = numberOfEffectivePixels + 16;  
  23.             break;  
  24.           }  
  25.         }  
  26.   
  27.         if ( numberOfEffectivePixels == 0 )  
  28.         {  
  29.           actualQP = g_RCInvalidQPValue;  
  30.         }  
  31.         else  
  32.         {  
  33.           actualQP = pcCU->getQP( 0 );  
  34.         }  
  35.         m_pcRdCost->setLambda(oldLambda);  
  36.   
  37. #if RATE_CONTROL_INTRA  
  38.         m_pcRateCtrl->getRCPic()->updateAfterLCU( m_pcRateCtrl->getRCPic()->getLCUCoded(), actualBits, actualQP, actualLambda,   
  39.           pcCU->getSlice()->getSliceType() == I_SLICE ? 0 : m_pcCfg->getLCULevelRC() );  
  40. #else  
  41.         m_pcRateCtrl->getRCPic()->updateAfterLCU( m_pcRateCtrl->getRCPic()->getLCUCoded(), actualBits, actualQP, actualLambda, m_pcCfg->getLCULevelRC() );  
  42. #endif  
  43.       }  
  44. #endif  
  45. #endif  


接着,在TEncGOP::compressGOP中,一个slice编码完后,进行一次更新:

[cpp] view plaincopy
  1. #if RATE_CONTROL_LAMBDA_DOMAIN  
  2.     if ( m_pcCfg->getUseRateCtrl() )  
  3.     {  
  4. #if !M0036_RC_IMPROVEMENT  
  5.       Double effectivePercentage = m_pcRateCtrl->getRCPic()->getEffectivePercentage();  
  6. #endif  
  7.       Double avgQP     = m_pcRateCtrl->getRCPic()->calAverageQP(); //!< arithmetic mean value for QP  
  8.       Double avgLambda = m_pcRateCtrl->getRCPic()->calAverageLambda(); //!< geometric mean value for lamda   
  9.       if ( avgLambda < 0.0 )  
  10.       {  
  11.         avgLambda = lambda;  
  12.       }  
  13. #if M0036_RC_IMPROVEMENT  
  14. #if RATE_CONTROL_INTRA  
  15.       m_pcRateCtrl->getRCPic()->updateAfterPicture( actualHeadBits, actualTotalBits, avgQP, avgLambda, pcSlice->getSliceType());  
  16. #else  
  17.       m_pcRateCtrl->getRCPic()->updateAfterPicture( actualHeadBits, actualTotalBits, avgQP, avgLambda );  
  18. #endif  
  19. #else  
  20.       m_pcRateCtrl->getRCPic()->updateAfterPicture( actualHeadBits, actualTotalBits, avgQP, avgLambda, effectivePercentage );  
  21. #endif  
  22.       m_pcRateCtrl->getRCPic()->addToPictureLsit( m_pcRateCtrl->getPicList() );  
  23.         
  24.       m_pcRateCtrl->getRCSeq()->updateAfterPic( actualTotalBits );  
  25.       if ( pcSlice->getSliceType() != I_SLICE )  
  26.       {  
  27.         m_pcRateCtrl->getRCGOP()->updateAfterPicture( actualTotalBits );  
  28.       }  
  29.       else    // for intra picture, the estimated bits are used to update the current status in the GOP  
  30.       {  
  31.         m_pcRateCtrl->getRCGOP()->updateAfterPicture( estimatedBits );  
  32.       }  
  33.     }  
  34. #else  
  35.     if(m_pcCfg->getUseRateCtrl())  
  36.     {  
  37.       UInt  frameBits = m_vRVM_RP[m_vRVM_RP.size()-1];  
  38.       m_pcRateCtrl->updataRCFrameStatus((Int)frameBits, pcSlice->getSliceType());  
  39.     }  
  40. #endif  


最后,在TEncTop::encode()中,对当前GOP的码控变量进行了销毁。

 

至此,总体框架大体分析完毕,最后,简单地提一下TEncRateCtrl.h中定义的类的关系:TEncRateCtrl属于RC顶层的控制,负责整个RC的流程管理;TEncRCSeq负责序列级的RC管理;TEncRCGOP负责GOP级的RC管理;TEncRCPic负责picture级的RC管理;TRCLCU负责LCU级的RC管理。


原创粉丝点击