参考阅读https://blog.csdn.net/HEVC_CJL/article/details/10982699和https://blog.csdn.net/NB_vol_1/article/details/56022073

最近重新回顾了HM里面的R-lambda码率控制,对应提案为JCTVC-K0103,已放在https://download.csdn.net/download/cztl520/85255894

是否使用码率控制(RC)由类TEncCfg中的成员变量m_RCEnableRateControl决定,需要手动在配置文件中开启。码率控制函数调用关系为:

1. 初始化

1.1 整个序列的码控参数初始化

这部分包含TEncRateCtrl和TEncRCSeq的初始化

在TEncTop::create()中,会对整个序列的码率控制进行初始化

  if ( m_RCEnableRateControl ){m_cRateCtrl.init( m_framesToBeEncoded, m_RCTargetBitrate, (Int)( (Double)m_iFrameRate/m_temporalSubsampleRatio + 0.5), m_iGOPSize, m_iSourceWidth, m_iSourceHeight,m_maxCUWidth, m_maxCUHeight,m_RCKeepHierarchicalBit, m_RCUseLCUSeparateModel, m_GOPList );}

1.1.1 TEncRateCtrl::init()

主要进行码率控制各种参数的初始化

Void TEncRateCtrl::init( Int totalFrames, Int targetBitrate, Int frameRate, Int GOPSize, Int picWidth, Int picHeight, Int LCUWidth, Int LCUHeight, Int keepHierBits, Bool useLCUSeparateModel, GOPEntry  GOPList[MAX_GOP] )
{destroy();// 默认低延时配置Bool isLowdelay = true;for ( Int i=0; i<GOPSize-1; i++ ){if ( GOPList[i].m_POC > GOPList[i+1].m_POC ){isLowdelay = false; // 此为随机接入配置break;}}Int numberOfLevel = 1;Int adaptiveBit = 0;if ( keepHierBits > 0 ){numberOfLevel = Int( log((Double)GOPSize)/log(2.0) + 0.5 ) + 1;}if ( !isLowdelay && GOPSize == 8 ){numberOfLevel = Int( log((Double)GOPSize)/log(2.0) + 0.5 ) + 1;}numberOfLevel++;    // intra picturenumberOfLevel++;    // non-reference pictureInt* bitsRatio; // 比特权重(w)bitsRatio = new Int[ GOPSize ];for ( Int i=0; i<GOPSize; i++ ){bitsRatio[i] = 10;if ( !GOPList[i].m_refPic ){bitsRatio[i] = 2;}}//如果采用分层编码,则每一帧的权重不同,这里的权重为每一帧获得比特数的比例if ( keepHierBits > 0 ){// K0103 式子(3)每像素的比特数,码率 / 帧率 = 一帧的比特数Double bpp = (Double)( targetBitrate / (Double)( frameRate*picWidth*picHeight ) );// K0103 table 1if ( GOPSize == 4 && isLowdelay ) {if ( bpp > 0.2 ){bitsRatio[0] = 2;bitsRatio[1] = 3;bitsRatio[2] = 2;bitsRatio[3] = 6;}else if( bpp > 0.1 ){bitsRatio[0] = 2;bitsRatio[1] = 3;bitsRatio[2] = 2;bitsRatio[3] = 10;}else if ( bpp > 0.05 ){bitsRatio[0] = 2;bitsRatio[1] = 3;bitsRatio[2] = 2;bitsRatio[3] = 12;}else{bitsRatio[0] = 2;bitsRatio[1] = 3;bitsRatio[2] = 2;bitsRatio[3] = 14;}if ( keepHierBits == 2 ){adaptiveBit = 1;}}// K0103 table 2else if ( GOPSize == 8 && !isLowdelay ){if ( bpp > 0.2 ){bitsRatio[0] = 15;bitsRatio[1] = 5;bitsRatio[2] = 4;bitsRatio[3] = 1;bitsRatio[4] = 1;bitsRatio[5] = 4;bitsRatio[6] = 1;bitsRatio[7] = 1;}else if ( bpp > 0.1 ){bitsRatio[0] = 20;bitsRatio[1] = 6;bitsRatio[2] = 4;bitsRatio[3] = 1;bitsRatio[4] = 1;bitsRatio[5] = 4;bitsRatio[6] = 1;bitsRatio[7] = 1;}else if ( bpp > 0.05 ){bitsRatio[0] = 25;bitsRatio[1] = 7;bitsRatio[2] = 4;bitsRatio[3] = 1;bitsRatio[4] = 1;bitsRatio[5] = 4;bitsRatio[6] = 1;bitsRatio[7] = 1;}else{bitsRatio[0] = 30;bitsRatio[1] = 8;bitsRatio[2] = 4;bitsRatio[3] = 1;bitsRatio[4] = 1;bitsRatio[5] = 4;bitsRatio[6] = 1;bitsRatio[7] = 1;}if ( keepHierBits == 2 ){adaptiveBit = 2;}}else{printf( "\n hierarchical bit allocation is not support for the specified coding structure currently.\n" );}}// GOPID到时域层的映射Int* GOPID2Level = new Int[ GOPSize ];for ( Int i=0; i<GOPSize; i++ ){GOPID2Level[i] = 1;if ( !GOPList[i].m_refPic ){GOPID2Level[i] = 2;}}if ( keepHierBits > 0 ){if ( GOPSize == 4 && isLowdelay ){GOPID2Level[0] = 3;GOPID2Level[1] = 2;GOPID2Level[2] = 3;GOPID2Level[3] = 1;}else if ( GOPSize == 8 && !isLowdelay ){GOPID2Level[0] = 1;GOPID2Level[1] = 2;GOPID2Level[2] = 3;GOPID2Level[3] = 4;GOPID2Level[4] = 4;GOPID2Level[5] = 3;GOPID2Level[6] = 4;GOPID2Level[7] = 4;}}if ( !isLowdelay && GOPSize == 8 ){GOPID2Level[0] = 1;GOPID2Level[1] = 2;GOPID2Level[2] = 3;GOPID2Level[3] = 4;GOPID2Level[4] = 4;GOPID2Level[5] = 3;GOPID2Level[6] = 4;GOPID2Level[7] = 4;}m_encRCSeq = new TEncRCSeq; // 创建序列级码率控制对象,并将初始化好的参数传给此对象m_encRCSeq->create( totalFrames, targetBitrate, frameRate, GOPSize, picWidth, picHeight, LCUWidth, LCUHeight, numberOfLevel, useLCUSeparateModel, adaptiveBit );m_encRCSeq->initBitsRatio( bitsRatio );m_encRCSeq->initGOPID2Level( GOPID2Level );m_encRCSeq->initPicPara(); // 初始化alpha和beta参数if ( useLCUSeparateModel ){m_encRCSeq->initLCUPara();}m_CpbSaturationEnabled = false;m_cpbSize              = targetBitrate;m_cpbState             = (UInt)(m_cpbSize*0.5f);m_bufferingRate        = (Int)(targetBitrate / frameRate);delete[] bitsRatio;delete[] GOPID2Level;
}

1.1.2 TEncRCSeq::create()

Void TEncRCSeq::create( Int totalFrames, Int targetBitrate, Int frameRate, Int GOPSize, Int picWidth, Int picHeight, Int LCUWidth, Int LCUHeight, Int numberOfLevel, Bool useLCUSeparateModel, Int adaptiveBit )
{destroy();m_totalFrames         = totalFrames;m_targetRate          = targetBitrate;m_frameRate           = frameRate;m_GOPSize             = GOPSize;m_picWidth            = picWidth;m_picHeight           = picHeight;m_LCUWidth            = LCUWidth;m_LCUHeight           = LCUHeight;m_numberOfLevel       = numberOfLevel;m_useLCUSeparateModel = useLCUSeparateModel;m_numberOfPixel   = m_picWidth * m_picHeight;// 码率 / 帧率 = 一帧的比特数,分配的目标总比特数,即输出码流大小m_targetBits      = (Int64)m_totalFrames * (Int64)m_targetRate / (Int64)m_frameRate;// 每像素被分到的目标比特m_seqTargetBpp = (Double)m_targetRate / (Double)m_frameRate / (Double)m_numberOfPixel;//m_alphaUpdate、m_betaUpdate这两个变量用于在接下来更新lamda的参数值if ( m_seqTargetBpp < 0.03 ){m_alphaUpdate = 0.01;m_betaUpdate  = 0.005;}else if ( m_seqTargetBpp < 0.08 ){m_alphaUpdate = 0.05;m_betaUpdate  = 0.025;}else if ( m_seqTargetBpp < 0.2 ){m_alphaUpdate = 0.1;m_betaUpdate  = 0.05;}else if ( m_seqTargetBpp < 0.5 ){m_alphaUpdate = 0.2;m_betaUpdate  = 0.1;}else{m_alphaUpdate = 0.4;m_betaUpdate  = 0.2;}// 平均每帧占用的目标比特数m_averageBits     = (Int)(m_targetBits / totalFrames);Int picWidthInBU  = ( m_picWidth  % m_LCUWidth  ) == 0 ? m_picWidth  / m_LCUWidth  : m_picWidth  / m_LCUWidth  + 1;Int picHeightInBU = ( m_picHeight % m_LCUHeight ) == 0 ? m_picHeight / m_LCUHeight : m_picHeight / m_LCUHeight + 1;m_numberOfLCU     = picWidthInBU * picHeightInBU;m_bitsRatio   = new Int[m_GOPSize];for ( Int i=0; i<m_GOPSize; i++ ){m_bitsRatio[i] = 1;}m_GOPID2Level = new Int[m_GOPSize];for ( Int i=0; i<m_GOPSize; i++ ){m_GOPID2Level[i] = 1;}m_picPara = new TRCParameter[m_numberOfLevel];// 每个图像层的alpha和beta参数值for ( Int i=0; i<m_numberOfLevel; i++ ){m_picPara[i].m_alpha = 0.0;m_picPara[i].m_beta  = 0.0;}if ( m_useLCUSeparateModel ) // 每个LCU的alpha和beta都有各自的值{m_LCUPara = new TRCParameter*[m_numberOfLevel];for ( Int i=0; i<m_numberOfLevel; i++ ){m_LCUPara[i] = new TRCParameter[m_numberOfLCU];for ( Int j=0; j<m_numberOfLCU; j++){m_LCUPara[i][j].m_alpha = 0.0;m_LCUPara[i][j].m_beta  = 0.0;}}}m_framesLeft = m_totalFrames; // 剩余帧数m_bitsLeft   = m_targetBits;  // 剩余可分配的比特数m_adaptiveBit = adaptiveBit;  m_lastLambda = 0.0;
}

1.2 GOP级码控参数初始化

TEncTop::encode()中

  if ( m_RCEnableRateControl ){m_cRateCtrl.initRCGOP( m_iNumPicRcvd );}

1.2.1 TEncRCGOP::create()

Void TEncRCGOP::create( TEncRCSeq* encRCSeq, Int numPic )
{destroy();Int targetBits = xEstGOPTargetBits( encRCSeq, numPic );// 一般不进入此if判断,除非开启adaptiveBitsif ( encRCSeq->getAdaptiveBits() > 0 && encRCSeq->getLastLambda() > 0.1 ){Double targetBpp = (Double)targetBits / encRCSeq->getNumPixel();Double basicLambda = 0.0;Double* lambdaRatio = new Double[encRCSeq->getGOPSize()];Double* equaCoeffA = new Double[encRCSeq->getGOPSize()];Double* equaCoeffB = new Double[encRCSeq->getGOPSize()];if ( encRCSeq->getAdaptiveBits() == 1 )   // for GOP size =4, low delay case{if ( encRCSeq->getLastLambda() < 120.0 ){lambdaRatio[1] = 0.725 * log( encRCSeq->getLastLambda() ) + 0.5793;lambdaRatio[0] = 1.3 * lambdaRatio[1];lambdaRatio[2] = 1.3 * lambdaRatio[1];lambdaRatio[3] = 1.0;}else{lambdaRatio[0] = 5.0;lambdaRatio[1] = 4.0;lambdaRatio[2] = 5.0;lambdaRatio[3] = 1.0;}}else if ( encRCSeq->getAdaptiveBits() == 2 )  // for GOP size = 8, random access case{if ( encRCSeq->getLastLambda() < 90.0 ){lambdaRatio[0] = 1.0;lambdaRatio[1] = 0.725 * log( encRCSeq->getLastLambda() ) + 0.7963;lambdaRatio[2] = 1.3 * lambdaRatio[1];lambdaRatio[3] = 3.25 * lambdaRatio[1];lambdaRatio[4] = 3.25 * lambdaRatio[1];lambdaRatio[5] = 1.3  * lambdaRatio[1];lambdaRatio[6] = 3.25 * lambdaRatio[1];lambdaRatio[7] = 3.25 * lambdaRatio[1];}else{lambdaRatio[0] = 1.0;lambdaRatio[1] = 4.0;lambdaRatio[2] = 5.0;lambdaRatio[3] = 12.3;lambdaRatio[4] = 12.3;lambdaRatio[5] = 5.0;lambdaRatio[6] = 12.3;lambdaRatio[7] = 12.3;}}xCalEquaCoeff( encRCSeq, lambdaRatio, equaCoeffA, equaCoeffB, encRCSeq->getGOPSize() );basicLambda = xSolveEqua( targetBpp, equaCoeffA, equaCoeffB, encRCSeq->getGOPSize() );encRCSeq->setAllBitRatio( basicLambda, equaCoeffA, equaCoeffB );delete []lambdaRatio;delete []equaCoeffA;delete []equaCoeffB;}m_picTargetBitInGOP = new Int[numPic];Int i;Int totalPicRatio = 0;Int currPicRatio = 0;for ( i=0; i<numPic; i++ ){totalPicRatio += encRCSeq->getBitRatio( i );}for ( i=0; i<numPic; i++ ){currPicRatio = encRCSeq->getBitRatio( i );// K0103 式子(9),注意:由于是初始化,式子中的CodedGOP等于0m_picTargetBitInGOP[i] = (Int)( ((Double)targetBits) * currPicRatio / totalPicRatio );}m_encRCSeq    = encRCSeq;m_numPic       = numPic;m_targetBits   = targetBits;m_picLeft      = m_numPic;m_bitsLeft     = m_targetBits;
}

1.2.2 TEncRCGOP::xEstGOPTargetBits()

Int TEncRCGOP::xEstGOPTargetBits( TEncRCSeq* encRCSeq, Int GOPSize )
{// 滑动窗口大小Int realInfluencePicture = min( g_RCSmoothWindowSize, encRCSeq->getFramesLeft() );// 平均每帧的比特数Int averageTargetBitsPerPic = (Int)( encRCSeq->getTargetBits() / encRCSeq->getTotalFrames() );// K0103 式(7)// TAvgPic,计算方法跟K0103不同,这里利用left的思路计算,而K0103利用coded的思路计算,但结果是一样的Int currentTargetBitsPerPic = (Int)( ( encRCSeq->getBitsLeft() - averageTargetBitsPerPic * (encRCSeq->getFramesLeft() - realInfluencePicture) ) / realInfluencePicture );// K0103 式(8)TGOPInt targetBits = currentTargetBitsPerPic * GOPSize;if ( targetBits < 200 ){targetBits = 200;   // at least allocate 200 bits for one GOP}return targetBits;
}

1.3 Picture级码控参数初始化

在TEncGOP::compressGOP()中,对Picture级的相关参数进行初始化

    if ( m_pcCfg->getUseRateCtrl() ) // TODO: does this work with multiple slices and slice-segments?{Int frameLevel = m_pcRateCtrl->getRCSeq()->getGOPID2Level( iGOPid );if ( pcPic->getSlice(0)->getSliceType() == I_SLICE ){frameLevel = 0;}// 初始化Picture参数m_pcRateCtrl->initRCPic( frameLevel );estimatedBits = m_pcRateCtrl->getRCPic()->getTargetBits();if (m_pcRateCtrl->getCpbSaturationEnabled() && frameLevel != 0){Int estimatedCpbFullness = m_pcRateCtrl->getCpbState() + m_pcRateCtrl->getBufferingRate();// prevent overflowif (estimatedCpbFullness - estimatedBits > (Int)(m_pcRateCtrl->getCpbSize()*0.9f)){estimatedBits = estimatedCpbFullness - (Int)(m_pcRateCtrl->getCpbSize()*0.9f);}estimatedCpbFullness -= m_pcRateCtrl->getBufferingRate();// prevent underflowif (estimatedCpbFullness - estimatedBits < m_pcRateCtrl->getRCPic()->getLowerBound()){estimatedBits = max(200, estimatedCpbFullness - m_pcRateCtrl->getRCPic()->getLowerBound());}m_pcRateCtrl->getRCPic()->setTargetBits(estimatedBits);}Int sliceQP = m_pcCfg->getInitialQP(); // 为配置文件中的InitialQP参数// 如果配置文件对序列第一帧指定了初始QP,则基于这个QP计算出lamdaif ( ( 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{m_pcSliceEncoder->calCostSliceI(pcPic); // TODO: This only analyses the first slice segment - what about the others?if ( m_pcCfg->getIntraPeriod() != 1 )   // do not refine allocated bits for all intra case{Int bits = m_pcRateCtrl->getRCSeq()->getLeftAverageBits();bits = m_pcRateCtrl->getRCPic()->getRefineBitsForIntra( bits );if (m_pcRateCtrl->getCpbSaturationEnabled() ){Int estimatedCpbFullness = m_pcRateCtrl->getCpbState() + m_pcRateCtrl->getBufferingRate();// prevent overflowif (estimatedCpbFullness - bits > (Int)(m_pcRateCtrl->getCpbSize()*0.9f)){bits = estimatedCpbFullness - (Int)(m_pcRateCtrl->getCpbSize()*0.9f);}estimatedCpbFullness -= m_pcRateCtrl->getBufferingRate();// prevent underflowif (estimatedCpbFullness - bits < m_pcRateCtrl->getRCPic()->getLowerBound()){bits = estimatedCpbFullness - m_pcRateCtrl->getRCPic()->getLowerBound();}}if ( bits < 200 ){bits = 200;}m_pcRateCtrl->getRCPic()->setTargetBits( bits );}list<TEncRCPic*> listPreviousPicture = m_pcRateCtrl->getPicList();m_pcRateCtrl->getRCPic()->getLCUInitTargetBits();lambda  = m_pcRateCtrl->getRCPic()->estimatePicLambda( listPreviousPicture, pcSlice->getSliceType());sliceQP = m_pcRateCtrl->getRCPic()->estimatePicQP( lambda, listPreviousPicture );}else    // normal case{list<TEncRCPic*> listPreviousPicture = m_pcRateCtrl->getPicList();lambda  = m_pcRateCtrl->getRCPic()->estimatePicLambda( listPreviousPicture, pcSlice->getSliceType());sliceQP = m_pcRateCtrl->getRCPic()->estimatePicQP( lambda, listPreviousPicture );}sliceQP = Clip3( -pcSlice->getSPS()->getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, sliceQP );m_pcRateCtrl->getRCPic()->setPicEstQP( sliceQP );//!< 设置当前slice使用的QP, lambda,编码时用到m_pcSliceEncoder->resetQP( pcPic, sliceQP, lambda );}

1.3.1 TEncRCPic::create()

Void TEncRCPic::create( TEncRCSeq* encRCSeq, TEncRCGOP* encRCGOP, Int frameLevel, list<TEncRCPic*>& listPreviousPictures )
{destroy();m_encRCSeq = encRCSeq;m_encRCGOP = encRCGOP;//!< K0103式子(9)Int targetBits    = xEstPicTargetBits( encRCSeq, encRCGOP );Int estHeaderBits = xEstPicHeaderBits( listPreviousPictures, frameLevel );if ( targetBits < estHeaderBits + 100 ){targetBits = estHeaderBits + 100;   // at least allocate 100 bits for picture data}m_frameLevel       = frameLevel;m_numberOfPixel    = encRCSeq->getNumPixel();m_numberOfLCU      = encRCSeq->getNumberOfLCU();m_estPicLambda     = 100.0;m_targetBits       = targetBits;m_estHeaderBits    = estHeaderBits;m_bitsLeft         = m_targetBits;Int picWidth       = encRCSeq->getPicWidth();Int picHeight      = encRCSeq->getPicHeight();Int LCUWidth       = encRCSeq->getLCUWidth();Int LCUHeight      = encRCSeq->getLCUHeight();Int picWidthInLCU  = ( picWidth  % LCUWidth  ) == 0 ? picWidth  / LCUWidth  : picWidth  / LCUWidth  + 1;Int picHeightInLCU = ( picHeight % LCUHeight ) == 0 ? picHeight / LCUHeight : picHeight / LCUHeight + 1;m_lowerBound       = xEstPicLowerBound( encRCSeq, encRCGOP );m_LCULeft         = m_numberOfLCU;m_bitsLeft       -= m_estHeaderBits;m_pixelsLeft      = m_numberOfPixel;m_LCUs           = new TRCLCU[m_numberOfLCU];Int i, j;Int LCUIdx;// 初始化每个LCU的参数for ( i=0; i<picWidthInLCU; i++ ){for ( j=0; j<picHeightInLCU; j++ ){LCUIdx = j*picWidthInLCU + i;m_LCUs[LCUIdx].m_actualBits = 0;m_LCUs[LCUIdx].m_QP         = 0;m_LCUs[LCUIdx].m_lambda     = 0.0;m_LCUs[LCUIdx].m_targetBits = 0;m_LCUs[LCUIdx].m_bitWeight  = 1.0;Int currWidth  = ( (i == picWidthInLCU -1) ? picWidth  - LCUWidth *(picWidthInLCU -1) : LCUWidth  );Int currHeight = ( (j == picHeightInLCU-1) ? picHeight - LCUHeight*(picHeightInLCU-1) : LCUHeight );m_LCUs[LCUIdx].m_numberOfPixel = currWidth * currHeight;}}m_picActualHeaderBits = 0;m_picActualBits       = 0;m_picQP               = 0;m_picLambda           = 0.0;
}

1.3.2 TEncRCPic::xEstPicTargetBits()

Int TEncRCPic::xEstPicTargetBits( TEncRCSeq* encRCSeq, TEncRCGOP* encRCGOP )
{Int targetBits        = 0;Int GOPbitsLeft       = encRCGOP->getBitsLeft();Int i;Int currPicPosition = encRCGOP->getNumPic()-encRCGOP->getPicLeft();Int currPicRatio    = encRCSeq->getBitRatio( currPicPosition );Int totalPicRatio   = 0;for ( i=currPicPosition; i<encRCGOP->getNumPic(); i++ ){totalPicRatio += encRCSeq->getBitRatio( i );}targetBits  = Int( ((Double)GOPbitsLeft) * currPicRatio / totalPicRatio );if ( targetBits < 100 ){targetBits = 100;   // at least allocate 100 bits for one picture}if ( m_encRCSeq->getFramesLeft() > 16 ){targetBits = Int( g_RCWeightPicRargetBitInBuffer * targetBits + g_RCWeightPicTargetBitInGOP * m_encRCGOP->getTargetBitInGOP( currPicPosition ) );}return targetBits;
}

1.3.3 TEncRCPic::xEstPicHeaderBits()

Int TEncRCPic::xEstPicHeaderBits( list<TEncRCPic*>& listPreviousPictures, Int frameLevel )
{Int numPreviousPics   = 0;Int totalPreviousBits = 0;list<TEncRCPic*>::iterator it;for ( it = listPreviousPictures.begin(); it != listPreviousPictures.end(); it++ ){if ( (*it)->getFrameLevel() == frameLevel ){totalPreviousBits += (*it)->getPicActualHeaderBits();numPreviousPics++;}}Int estHeaderBits = 0;if ( numPreviousPics > 0 ){estHeaderBits = totalPreviousBits / numPreviousPics;}return estHeaderBits;
}

1.3.4 TEncRCPic::estimatePicLambda()

此函数计算帧级lambda

Double TEncRCPic::estimatePicLambda( list<TEncRCPic*>& listPreviousPictures, SliceType eSliceType)
{Double alpha         = m_encRCSeq->getPicPara( m_frameLevel ).m_alpha;Double beta          = m_encRCSeq->getPicPara( m_frameLevel ).m_beta;Double bpp       = (Double)m_targetBits/(Double)m_numberOfPixel;Double estLambda;if (eSliceType == I_SLICE){estLambda = calculateLambdaIntra(alpha, beta, pow(m_totalCostIntra/(Double)m_numberOfPixel, BETA1), bpp);}else{estLambda = alpha * pow( bpp, beta ); //!< K0103 式子(10)}Double lastLevelLambda = -1.0;Double lastPicLambda   = -1.0;Double lastValidLambda = -1.0;list<TEncRCPic*>::iterator it;for ( it = listPreviousPictures.begin(); it != listPreviousPictures.end(); it++ ){if ( (*it)->getFrameLevel() == m_frameLevel ){lastLevelLambda = (*it)->getPicActualLambda();}lastPicLambda     = (*it)->getPicActualLambda();if ( lastPicLambda > 0.0 ){lastValidLambda = lastPicLambda;}}//!< 以下对lastLevelLambda和estLambda进行clip,范围在K0103的section 3.2中进行了定义if ( lastLevelLambda > 0.0 ){lastLevelLambda = Clip3( 0.1, 10000.0, lastLevelLambda );estLambda = Clip3( lastLevelLambda * pow( 2.0, -3.0/3.0 ), lastLevelLambda * pow( 2.0, 3.0/3.0 ), estLambda );}if ( lastPicLambda > 0.0 ){lastPicLambda = Clip3( 0.1, 2000.0, lastPicLambda );estLambda = Clip3( lastPicLambda * pow( 2.0, -10.0/3.0 ), lastPicLambda * pow( 2.0, 10.0/3.0 ), estLambda );}else if ( lastValidLambda > 0.0 ){lastValidLambda = Clip3( 0.1, 2000.0, lastValidLambda );estLambda = Clip3( lastValidLambda * pow(2.0, -10.0/3.0), lastValidLambda * pow(2.0, 10.0/3.0), estLambda );}else{estLambda = Clip3( 0.1, 10000.0, estLambda );}if ( estLambda < 0.1 ){estLambda = 0.1;}m_estPicLambda = estLambda;Double totalWeight = 0.0;// initial BU bit allocation weightfor ( Int i=0; i<m_numberOfLCU; i++ ){Double alphaLCU, betaLCU;if ( m_encRCSeq->getUseLCUSeparateModel() ){alphaLCU = m_encRCSeq->getLCUPara( m_frameLevel, i ).m_alpha;betaLCU  = m_encRCSeq->getLCUPara( m_frameLevel, i ).m_beta;}else{alphaLCU = m_encRCSeq->getPicPara( m_frameLevel ).m_alpha;betaLCU  = m_encRCSeq->getPicPara( m_frameLevel ).m_beta;}m_LCUs[i].m_bitWeight =  m_LCUs[i].m_numberOfPixel * pow( estLambda/alphaLCU, 1.0/betaLCU );if ( m_LCUs[i].m_bitWeight < 0.01 ){m_LCUs[i].m_bitWeight = 0.01;}totalWeight += m_LCUs[i].m_bitWeight;}for ( Int i=0; i<m_numberOfLCU; i++ ){Double BUTargetBits = m_targetBits * m_LCUs[i].m_bitWeight / totalWeight;m_LCUs[i].m_bitWeight = BUTargetBits;}return estLambda;
}

1.3.5 TEncRCPic::estimatePicQP()

此函数计算帧级QP

Int TEncRCPic::estimatePicQP( Double lambda, list<TEncRCPic*>& listPreviousPictures )
{Int QP = Int( 4.2005 * log( lambda ) + 13.7122 + 0.5 ); // 经典R-λ模型Int lastLevelQP = g_RCInvalidQPValue;Int lastPicQP   = g_RCInvalidQPValue;Int lastValidQP = g_RCInvalidQPValue;list<TEncRCPic*>::iterator it;for ( it = listPreviousPictures.begin(); it != listPreviousPictures.end(); it++ ){if ( (*it)->getFrameLevel() == m_frameLevel ){lastLevelQP = (*it)->getPicActualQP();}lastPicQP = (*it)->getPicActualQP();if ( lastPicQP > g_RCInvalidQPValue ){lastValidQP = lastPicQP;}}//!< 以下对QP进行clip,范围在K0103的section 3.2进行了定义if ( lastLevelQP > g_RCInvalidQPValue ){QP = Clip3( lastLevelQP - 3, lastLevelQP + 3, QP );}if( lastPicQP > g_RCInvalidQPValue ){QP = Clip3( lastPicQP - 10, lastPicQP + 10, QP );}else if( lastValidQP > g_RCInvalidQPValue ){QP = Clip3( lastValidQP - 10, lastValidQP + 10, QP );}return QP;
}

1.4 LCU级码控参数初始化

在TEncSlice::compressSlice()中,对LCU级的相关参数进行初始化

    if ( m_pcCfg->getUseRateCtrl() ) // LCU的RC参数初始化{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() ){// 如果为I帧并且启用了RCForceIntraQP,或者没有启用LCULevelRateControl,则LCU直接使用当前slice的QPestQP = 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 RDOQconst Double chromaLambda = estLambda / m_pcRdCost->getChromaWeight();const Double lambdaArray[MAX_NUM_COMPONENT] = { estLambda, chromaLambda, chromaLambda };m_pcTrQuant->setLambdas( lambdaArray );
#elsem_pcTrQuant->setLambda( estLambda );
#endif}m_pcRateCtrl->setRCQP( estQP );
#if ADAPTIVE_QP_SELECTIONpCtu->getSlice()->setSliceQpBase( estQP ); //!< 设置编码时使用的QP值
#endif}

1.4.1 TEncRCPic::getLCUTargetBpp()

此函数计算LCU的bpp

Double TEncRCPic::getLCUTargetBpp(SliceType eSliceType)
{Int   LCUIdx    = getLCUCoded();Double bpp      = -1.0; Int avgBits     = 0;if (eSliceType == I_SLICE){Int noOfLCUsLeft = m_numberOfLCU - LCUIdx + 1;Int bitrateWindow = min(4,noOfLCUsLeft);       // 类似于之前的滑动窗口Double MAD      = getLCU(LCUIdx).m_costIntra;  //计算出每个LCU对应的MAD值// m_remainingCostIntra为当前帧的总MAD// m_remainingCostIntra = m_totalCostIntra;if (m_remainingCostIntra > 0.1 ){Double weightedBitsLeft = (m_bitsLeft*bitrateWindow+(m_bitsLeft-getLCU(LCUIdx).m_targetBitsLeft)*noOfLCUsLeft)/(Double)bitrateWindow;avgBits = Int( MAD*weightedBitsLeft/m_remainingCostIntra );}else{avgBits = Int( m_bitsLeft / m_LCULeft );}//分配完一个LCU比特后,更新剩余的m_remainingCostIntram_remainingCostIntra -= MAD;}else // 非I帧{Double totalWeight = 0;for ( Int i=LCUIdx; i<m_numberOfLCU; i++ ){totalWeight += m_LCUs[i].m_bitWeight;}Int realInfluenceLCU = min( g_RCLCUSmoothWindowSize, getLCULeft() );avgBits = (Int)( m_LCUs[LCUIdx].m_bitWeight - ( totalWeight - m_bitsLeft ) / realInfluenceLCU + 0.5 );}if ( avgBits < 1 ){avgBits = 1;}bpp = ( Double )avgBits/( Double )m_LCUs[ LCUIdx ].m_numberOfPixel;m_LCUs[ LCUIdx ].m_targetBits = avgBits;return bpp;
}

1.4.2 TEncRCPic::getLCUEstLambda()

此函数计算LCU的lambda

Double TEncRCPic::getLCUEstLambda( Double bpp )
{Int   LCUIdx = getLCUCoded();Double alpha;Double beta;if ( m_encRCSeq->getUseLCUSeparateModel() ) //!< enable LCU level RC{alpha = m_encRCSeq->getLCUPara( m_frameLevel, LCUIdx ).m_alpha;beta  = m_encRCSeq->getLCUPara( m_frameLevel, LCUIdx ).m_beta;}else //!< 只进行picture level的RC,故alpha,beta使用的是picture level的值{alpha = m_encRCSeq->getPicPara( m_frameLevel ).m_alpha;beta  = m_encRCSeq->getPicPara( m_frameLevel ).m_beta;}Double estLambda = alpha * pow( bpp, beta );//for Lambda clip, picture level clipDouble clipPicLambda = m_estPicLambda;//for Lambda clip, LCU level clipDouble clipNeighbourLambda = -1.0;for ( Int i=LCUIdx - 1; i>=0; i-- ){if ( m_LCUs[i].m_lambda > 0 ){clipNeighbourLambda = m_LCUs[i].m_lambda;break;}}//!< 在K0103的section 3.2中进行了定义if ( clipNeighbourLambda > 0.0 ){estLambda = Clip3( clipNeighbourLambda * pow( 2.0, -1.0/3.0 ), clipNeighbourLambda * pow( 2.0, 1.0/3.0 ), estLambda );}if ( clipPicLambda > 0.0 ){estLambda = Clip3( clipPicLambda * pow( 2.0, -2.0/3.0 ), clipPicLambda * pow( 2.0, 2.0/3.0 ), estLambda );}else{estLambda = Clip3( 10.0, 1000.0, estLambda );}if ( estLambda < 0.1 ){estLambda = 0.1;}return estLambda;
}

1.4.3 TEncRCPic::getLCUEstQP()

此函数计算LCU的QP

Int TEncRCPic::getLCUEstQP( Double lambda, Int clipPicQP )
{Int LCUIdx = getLCUCoded();Int estQP = Int( 4.2005 * log( lambda ) + 13.7122 + 0.5 );//for Lambda clip, LCU level clipInt clipNeighbourQP = g_RCInvalidQPValue;for ( Int i=LCUIdx - 1; i>=0; i-- ){if ( (getLCU(i)).m_QP > g_RCInvalidQPValue ){clipNeighbourQP = getLCU(i).m_QP;break;}}//!< 在K0103的section 3.2中进行了定义if ( clipNeighbourQP > g_RCInvalidQPValue ){estQP = Clip3( clipNeighbourQP - 1, clipNeighbourQP + 1, estQP );}estQP = Clip3( clipPicQP - 2, clipPicQP + 2, estQP );return estQP;
}

2. 参数更新

2.1 编码完一个LCU后,进行参数值更新

在TEncSlice::compressSlice()中

    if ( m_pcCfg->getUseRateCtrl() ) // 每编码完一个LCU,进行一次更新{Int actualQP        = g_RCInvalidQPValue;Double actualLambda = m_pcRdCost->getLambda();Int actualBits      = pCtu->getTotalBits();Int numberOfEffectivePixels    = 0;for ( Int idx = 0; idx < pcPic->getNumPartitionsInCtu(); idx++ ){//!< 不考虑skip模式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() );}

2.1.1 TEncRCPic::updateAfterLCU()

更新LCU级参数:剩余的比特数及α、β等

Void TEncRCPic::updateAfterCTU( Int LCUIdx, Int bits, Int QP, Double lambda, Bool updateLCUParameter )
{m_LCUs[LCUIdx].m_actualBits = bits;m_LCUs[LCUIdx].m_QP         = QP;m_LCUs[LCUIdx].m_lambda     = lambda;m_LCULeft--;m_bitsLeft   -= bits; // frame中剩余比特数的更新m_pixelsLeft -= m_LCUs[LCUIdx].m_numberOfPixel;if ( !updateLCUParameter ){return;}if ( !m_encRCSeq->getUseLCUSeparateModel() ){return;}Double alpha = m_encRCSeq->getLCUPara( m_frameLevel, LCUIdx ).m_alpha;Double beta  = m_encRCSeq->getLCUPara( m_frameLevel, LCUIdx ).m_beta;// 按照公式(12)-(13)更新参数Int LCUActualBits   = m_LCUs[LCUIdx].m_actualBits;Int LCUTotalPixels  = m_LCUs[LCUIdx].m_numberOfPixel;Double bpp         = ( Double )LCUActualBits/( Double )LCUTotalPixels;Double calLambda   = alpha * pow( bpp, beta );Double inputLambda = m_LCUs[LCUIdx].m_lambda;if( inputLambda < 0.01 || calLambda < 0.01 || bpp < 0.0001 ){alpha *= ( 1.0 - m_encRCSeq->getAlphaUpdate() / 2.0 );beta  *= ( 1.0 - m_encRCSeq->getBetaUpdate() / 2.0 );alpha = Clip3( g_RCAlphaMinValue, g_RCAlphaMaxValue, alpha );beta  = Clip3( g_RCBetaMinValue,  g_RCBetaMaxValue,  beta  );TRCParameter rcPara;rcPara.m_alpha = alpha;rcPara.m_beta  = beta;m_encRCSeq->setLCUPara( m_frameLevel, LCUIdx, rcPara );return;}calLambda = Clip3( inputLambda / 10.0, inputLambda * 10.0, calLambda );alpha += m_encRCSeq->getAlphaUpdate() * ( log( inputLambda ) - log( calLambda ) ) * alpha;Double lnbpp = log( bpp );lnbpp = Clip3( -5.0, -0.1, lnbpp );beta  += m_encRCSeq->getBetaUpdate() * ( log( inputLambda ) - log( calLambda ) ) * lnbpp;alpha = Clip3( g_RCAlphaMinValue, g_RCAlphaMaxValue, alpha );beta  = Clip3( g_RCBetaMinValue,  g_RCBetaMaxValue,  beta  );TRCParameter rcPara;rcPara.m_alpha = alpha;rcPara.m_beta  = beta;m_encRCSeq->setLCUPara( m_frameLevel, LCUIdx, rcPara );}

2.2 编码完一帧后,进行参数更新

TEncGOP::compressGOP()中

    if ( m_pcCfg->getUseRateCtrl() ){// 每个LCU都有自己的λ和QP// 整帧的λ为所有LCU的λ的几何平均值// 整帧的QP为所有LCU的QP的算术平均值Double avgQP     = m_pcRateCtrl->getRCPic()->calAverageQP();Double avgLambda = m_pcRateCtrl->getRCPic()->calAverageLambda();if ( avgLambda < 0.0 ){avgLambda = lambda;}m_pcRateCtrl->getRCPic()->updateAfterPicture( actualHeadBits, actualTotalBits, avgQP, avgLambda, pcSlice->getSliceType());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 );}if (m_pcRateCtrl->getCpbSaturationEnabled()){m_pcRateCtrl->updateCpbState(actualTotalBits);printf(" [CPB %6d bits]", m_pcRateCtrl->getCpbState());}}

2.2.1 TEncRCPic::updateAfterPictur()

更新Picture级参数:α、β等

Void TEncRCPic::updateAfterPicture( Int actualHeaderBits, Int actualTotalBits, Double averageQP, Double averageLambda, SliceType eSliceType)
{m_picActualHeaderBits = actualHeaderBits;m_picActualBits       = actualTotalBits;if ( averageQP > 0.0 ){m_picQP             = Int( averageQP + 0.5 );}else{m_picQP             = g_RCInvalidQPValue;}m_picLambda           = averageLambda;Double alpha = m_encRCSeq->getPicPara( m_frameLevel ).m_alpha;Double beta  = m_encRCSeq->getPicPara( m_frameLevel ).m_beta;if (eSliceType == I_SLICE){updateAlphaBetaIntra(&alpha, &beta);}else{// update parameters// 按照公式(11)-(13)计算Double picActualBits = ( Double )m_picActualBits;Double picActualBpp  = picActualBits/(Double)m_numberOfPixel;Double calLambda     = alpha * pow( picActualBpp, beta );Double inputLambda   = m_picLambda;if ( inputLambda < 0.01 || calLambda < 0.01 || picActualBpp < 0.0001 ){alpha *= ( 1.0 - m_encRCSeq->getAlphaUpdate() / 2.0 );beta  *= ( 1.0 - m_encRCSeq->getBetaUpdate() / 2.0 );alpha = Clip3( g_RCAlphaMinValue, g_RCAlphaMaxValue, alpha );beta  = Clip3( g_RCBetaMinValue,  g_RCBetaMaxValue,  beta  );TRCParameter rcPara;rcPara.m_alpha = alpha;rcPara.m_beta  = beta;m_encRCSeq->setPicPara( m_frameLevel, rcPara );return;}calLambda = Clip3( inputLambda / 10.0, inputLambda * 10.0, calLambda );alpha += m_encRCSeq->getAlphaUpdate() * ( log( inputLambda ) - log( calLambda ) ) * alpha;Double lnbpp = log( picActualBpp );lnbpp = Clip3( -5.0, -0.1, lnbpp );beta  += m_encRCSeq->getBetaUpdate() * ( log( inputLambda ) - log( calLambda ) ) * lnbpp;alpha = Clip3( g_RCAlphaMinValue, g_RCAlphaMaxValue, alpha );beta  = Clip3( g_RCBetaMinValue,  g_RCBetaMaxValue,  beta  );}TRCParameter rcPara;rcPara.m_alpha = alpha;rcPara.m_beta  = beta;m_encRCSeq->setPicPara( m_frameLevel, rcPara );if ( m_frameLevel == 1 ){Double currLambda = Clip3( 0.1, 10000.0, m_picLambda );Double updateLastLambda = g_RCWeightHistoryLambda * m_encRCSeq->getLastLambda() + g_RCWeightCurrentLambda * currLambda;m_encRCSeq->setLastLambda( updateLastLambda );}
}

2.2.2 TEncRCSeq::updateAfterPic()

更新剩余比特数和帧数

Void TEncRCSeq::updateAfterPic ( Int bits )
{m_bitsLeft -= bits;m_framesLeft--;
}

2.2.3 TEncRCGOP::updateAfterPicture()

更新剩余比特数和帧数

Void TEncRCGOP::updateAfterPicture( Int bitsCost )
{m_bitsLeft -= bitsCost;m_picLeft--;
}

HEVC码率控制代码分析相关推荐

  1. PX4代码学习系列博客(6)——offboard模式位置控制代码分析(之前转载过,这是第二次转载了)

    我刚刚发现这篇文章去年八月份的时候转载过一次了 https://blog.csdn.net/sinat_16643223/article/details/107874349 转载自:https://b ...

  2. HEVC码率控制资料整理

    本篇博客主要记录项目过程中参考过.写得比较好的博客. 下载 HEVC项目 YUV视频文件 安装与使用 https://lin-lz.blog.csdn.net/article/details/5277 ...

  3. HEVC码率控制TEncRCPic

    TEncRCPic帧级别码率控制 先开看成员属性 补充:m_totalCostIntra是指该帧(I帧)的帧内所有LCU的代价总和,该值通过TEncSlice::calCostSliceI函数赋值,同 ...

  4. x264参数介绍(帧类型和码率控制,分析和视频可用性信息)

    鉴于x264的参数众多,各种参数的配合复杂,为了使用者方便,x264建议如无特别需要可使用preset和tune设置.这套开发者推荐的参数较为合理,可在此基础上在调整一些具体参数以符合自己需要,手动设 ...

  5. 【UAV】高度控制代码分析

    文章目录 代码分析 MotorControl() 控制流程图 代码分析 MotorControl() case PROCESS_31: {int16_t thr_temp;// 这里的定高标志在 Mo ...

  6. HEVC码率控制介绍(R-Lamda)

    本来是不想贴出相应的代码的,但是还是贴出来大概的模块吧,这样才能不纸上谈兵! R-lamda模型提出到优化已有2年,从近几年的文章来看,大体归为以下几类:一类是帧内的码率控制算法,一类是模型参数更新, ...

  7. VCIP2020:面向机器视觉的HEVC码率控制

    本文来自VCIP2020文章<A Novel Visual Analysis Oriented Rate Control Scheme for HEVC> 深度学习的发展使得计算机视觉任务 ...

  8. X264码率控制流程分析

    码率控制的理论知识: 码率控制的目的和意义: 图像通信中码率控制的目的:通过调节编码参数,控制单位时间内的编码视频流的数据量,以使产生的比特流符合各种应用的需求.视频压缩的效率和视频内容有很大的关系, ...

  9. vtm编码划分_VTM码率控制——代码学习三

    文章目录 码率分配 GOP码率分配 int EncRCGOP::xEstGOPTargetBits( EncRCSeq* encRCSeq, int GOPSize ) { int realInflu ...

最新文章

  1. wpf异形按钮,定制异型按钮在WPF
  2. beats 耳机 android,Beats耳机app
  3. OUYA设备的购买和安装
  4. 从入门到精通系列Java高级工程师路线介绍,附答案
  5. ×××技术在ATM机无线组网中的应用
  6. linux中内部命令有哪些,linux内部命令有哪些
  7. 为什么机油使用后变红_水泥固化剂的使用原理是什么,涂洒后时间为什么要足够长?...
  8. 看视频课程的正确方法
  9. graphviz安装以及入门
  10. NLP学习01--BP神经网络
  11. delphixe10linux,减小Delphi XE 以上版 编译出来的程序体积
  12. 移远ec20 openLinux交叉编译python
  13. ngrok跟小米球的使用
  14. 周鸿祎——互联网业界的“搅局者”
  15. c语言ntc程序,NTC热敏电阻程序.doc
  16. sap 新增科目表_在SAP中新建会计科目
  17. oh-my-zsh主题添加命令显示执行时间和当前时间
  18. PC版免费京东全民营业自动化做任务脚本(多号版)
  19. win10双显卡开机黑屏时间长
  20. 根号 巴比伦_建立巴比伦卫生设计系统

热门文章

  1. 每月自评之四:2013年4月
  2. 2D图像处理:孔洞填充
  3. 喜玛拉雅——徐薇翻唱合集
  4. 如何沿法线方向挤出面
  5. 华为路由交换课程笔记10-GARP和GVRP
  6. fastjson 是反射吗_6种超声检测灵敏度,你都知道吗?
  7. telegram 常见问题
  8. 解决导出excel文件名中文乱码的问题
  9. core dump 是什么意思?
  10. Linux中的lo回环接口详细介绍