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()中,对整个序列都要用到的相关参数进行初始化
- #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 );
- }
- m_cGOPEncoder.compressGOP(m_iPOCLast, m_iNumPicRcvd, m_cListPic, rcListPicYuvRecOut, accessUnitsOut, false, false);
- #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
- m_pcSliceEncoder->precompressSlice( pcPic );
m_pcSliceEncoder->compressSlice ( pcPic ); //并没有紧跟着GOP的初始化,但是也是在当前函数中
最后,在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管理。
- HEVC码率控制浅析——HM代码阅读之一
- HEVC码率控制浅析——HM代码阅读之一
- HEVC码率控制浅析——HM代码阅读之二
- HEVC码率控制浅析——HM代码阅读之三
- HEVC码率控制浅析——HM代码阅读之四
- HEVC码率控制浅析——HM代码阅读之二
- HEVC码率控制浅析——HM代码阅读之三
- HEVC码率控制浅析——HM代码阅读之四
- HM编码器代码阅读(29)——码率控制
- HEVC码率控制算法研究与HM相应代码分析(三)——算法及代码分析
- HEVC码率控制算法研究与HM相应代码分析(一)——HEVC标准及编码流程介绍
- HM编码器代码阅读(41)——码率控制(二)
- HEVC码率控制算法研究与HM相应代码分析(二)——新的码率控制模型
- HM编码器代码阅读(21)——熵编码的概念以及在HEVC中应用
- HM编码器代码阅读(42)——率失真优化
- HEVC逆扫描之一:在HM代码中的位置
- HEVC学习 —— HM的使用
- HEVC学习 —— HM的使用
- 计算机图形学 -- 像素点的生成 [非常详细的解释~]
- dbms_workload_repository手工管理AWR
- APP支持请回复相关主题帖或邮件至 developer.haipeng.zhao@gmail.com
- 回头看看走过的路,花开花落,很坦然
- debug assertion failed! winocc.cpp line 381
- HEVC码率控制浅析——HM代码阅读之一
- 移植EMCV到DM6467(5)——修改encodedecode demo测试算法封装
- 软件工程基础 八
- 网络性能测试工具iperf编译记录
- 百度诉360违反Robots协议 索赔1亿元
- 发表歌剧院
- 阅读《剑指offer》第三章
- win7无法睡眠解决办法之一
- linux 常用命令