在前两篇文章中,首先介绍了HEVC标准和编码流程,然后介绍了在HEVC中采用的全新的R-λ模型,本文将基于前面的内容和相应代码对码率控制算法进行详细的分析。
下面基于JCTVC-K0103提案详细介绍一下HEVC中基于R-λ模型的码率控制方法。同时基于HM-10对码率控制部分的代码做一个简要分析,相比于JM,HM中更多的使用了面向对象技术,结构更加清楚明了,码率控制相关代码的基本调用层次如下,纵向上即层层调用的关系,横向上是对几个比较重要的函数的内部调用情况列了出来。
跟以前的方法类似,码率控制方法还是分为两大步骤:比特分配以及调整编码参数来达到目标码率,在第二步中才会用到R-λ模型。
下面先看比特分配。分为三个级别,GOP层、图片层和基本编码单元层。
首先计算每幅图片的目标比特数,f为帧率,Rtar为目标码率
设已编码图片的数量为Ncoded,这些图片用掉的比特数为Rcoded,当前GOP中的图片数量为NGOP ,SW是平滑比特分配的滑动窗口的大小,用于使得比特消耗变化和编码图片的质量更加平缓,在这里设为40,则GOP级别的比特分配为
我们希望能在SW帧之后达到目标码率,如果SW帧图片可以正好做到每一帧消耗TAvgPic 比特,则上式可以改写为
式子的第一部分代表目标码率,第二部分则代表buffer的状态
对应代码如下
main TAppEncTop::encode TEncTop::encode TEncRateCtrl::initRCGOP TEncRCGOP::create TEncRCGOP:: xEstGOPTargetBits 事先定义有 const Int g_RCSmoothWindowSize = 40; Int TEncRCGOP::xEstGOPTargetBits( TEncRCSeq* encRCSeq, Int GOPSize ) { Int realInfluencePicture = min( g_RCSmoothWindowSize, encRCSeq->getFramesLeft() ); Int averageTargetBitsPerPic = (Int)( encRCSeq->getTargetBits() / encRCSeq->getTotalFrames() ); Int currentTargetBitsPerPic = (Int)( ( encRCSeq->getBitsLeft() - averageTargetBitsPerPic * (encRCSeq->getFramesLeft() - realInfluencePicture) ) / realInfluencePicture ); Int targetBits = currentTargetBitsPerPic * GOPSize; if ( targetBits < 200 ) { targetBits = 200; // at least allocate 200 bits for one GOP } return targetBits; }
main TAppEncTop::encode TEncTop::encode(先initRCGOP再compressGOP) TEncGOP::compressGOP TEncRateCtrl::initRCPic TEncRCPic::create 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( 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; } 同时有 if ( targetBits < estHeaderBits + 100 ) { targetBits = estHeaderBits + 100; // at least allocate 100 bits for picture data }
在实际的应用中,所有图片均使用相同的ω是一种选择(即Equal allocation),这样设置会导致每幅图片消耗的比特差别不大。图片之间分级分配比特是另一种不错的选择(Hierarchical allocation),因为图片之间分级的分配比特可以对编码性能带来不小的提升。K0103的码率控制算法支持均匀分配比特和分级分配比特。
Main TAppEncTop::encode TAppEncTop::xCreateLib TEncTop::create TEncRateCtrl::init Int* bitsRatio; bitsRatio = new Int[ GOPSize ]; for ( Int i=0; i<GOPSize; i++ ) { bitsRatio[i] = 10; if ( !GOPList[i].m_refPic ) { bitsRatio[i] = 2; } } if ( keepHierBits ) { Double bpp = (Double)( targetBitrate / (Double)( frameRate*picWidth*picHeight ) ); if ( 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; } } else 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; } } else { printf( "\n hierarchical bit allocation is not support for the specified coding structure currently." ); } }
需要注意的是,该修正值只在更新Rcoded 时使用(整个序列消耗的比特数),而不会用于更新CodedGOP (当前GOP消耗的比特数),这是因为帧内编码帧消耗的比特数往往很高,甚至高于给GOP分配的比特数,用未经修正的TCurrPic值更新CodedGOP
main TAppEncTop::encode TEncTop::encode(先initRCGOP再compressGOP) TEncGOP::compressGOP TEncRCSeq::getRefineBitsForIntra Int TEncRCSeq::getRefineBitsForIntra( Int orgBits ) { Double bpp = ( (Double)orgBits ) / m_picHeight / m_picHeight; if ( bpp > 0.2 ) { return orgBits * 5; } if ( bpp > 0.1 ) { return orgBits * 7; } return orgBits * 10; }
Bitheader 是所有头信息比特数的估计值,由同一层之前的已编码图片的实际头信息比特数估计得来。
main TAppEncTop::encode TEncTop::encode(先initRCGOP再compressGOP) TEncGOP::compressGOP TEncRateCtrl::initRCPic TEncRCPic::create(先xEstPicTargetBits再xEstPicHeaderBits) 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; }
以上就是比特分配的过程。
main TAppEncTop::encode TEncTop::encode(先initRCGOP再compressGOP) TEncGOP::compressGOP TEncSlice::compressSlice TEncRCPic::getLCUTargetBpp Double TEncRCPic::getLCUTargetBpp() { Int LCUIdx = getLCUCoded(); Double bpp = -1.0; Int avgBits = 0; Double totalMAD = -1.0; Double MAD = -1.0; if ( m_lastPicture == NULL ) { avgBits = Int( m_bitsLeft / m_LCULeft ); } else { MAD = m_lastPicture->getLCU(LCUIdx).m_MAD; totalMAD = m_lastPicture->getTotalMAD(); for ( Int i=0; i<LCUIdx; i++ ) { totalMAD -= m_lastPicture->getLCU(i).m_MAD; } if ( totalMAD > 0.1 ) { avgBits = Int( m_bitsLeft * MAD / totalMAD ); } else { avgBits = Int( m_bitsLeft / m_LCULeft ); } } #if L0033_RC_BUGFIX if ( avgBits < 1 ) { avgBits = 1; } #else if ( avgBits < 5 ) { avgBits = 5; } #endif bpp = ( Double )avgBits/( Double )m_LCUs[ LCUIdx ].m_numberOfPixel; m_LCUs[ LCUIdx ].m_targetBits = avgBits; return bpp; }
使用上式依据一幅图片或者一个LCU的目标码率尺推导得到当前图片或者当前LCU编码所需要使用的λ。现在唯一的问题是,在不同编码序列的情况下,模型可能会拥有完全不相同的α和β值。此外,即使对于同一序列,处于不同级别的图片也可能拥有完全不相同的α和β值。例如,当GOP大小为4时,图片共分为三个级别,这三个级别的图片的α和β值可能是不同的。另外,不同的基本编码单元也可能拥有不同的α和β值,在此,我们假设在同一级别图片中对应位置的基本编码单元的α和β值相同。
需要注意的是α和β值的初始值设置并不是很严重的问题,因为在编码过程中,α和β值会根据序列逐渐更新,并最终适应序列特性。
设α和β值的初始值分别为3.2003 和-1.367。
Main TAppEncTop::encode TAppEncTop::xCreateLib TEncTop::create TEncRateCtrl::init TEncRCSeq::initPicPara Void TEncRCSeq::initPicPara( TRCParameter* picPara ) { assert( m_picPara != NULL ); if ( picPara == NULL ) { for ( Int i=0; i<m_numberOfLevel; i++ ) { m_picPara[i].m_alpha = 3.2003; m_picPara[i].m_beta = -1.367; } } else { for ( Int i=0; i<m_numberOfLevel; i++ ) { m_picPara[i] = picPara[i]; } } } 以及 Main TAppEncTop::encode TAppEncTop::xCreateLib TEncTop::create TEncRateCtrl::init TEncRCSeq::initLCUPara Void TEncRCSeq::initLCUPara( TRCParameter** LCUPara ) { if ( m_LCUPara == NULL ) { return; } if ( LCUPara == NULL ) { for ( Int i=0; i<m_numberOfLevel; i++ ) { for ( Int j=0; j<m_numberOfLCU; j++) { m_LCUPara[i][j].m_alpha = 3.2003; m_LCUPara[i][j].m_beta = -1.367; } } } else { for ( Int i=0; i<m_numberOfLevel; i++ ) { for ( Int j=0; j<m_numberOfLCU; j++) { m_LCUPara[i][j] = LCUPara[i][j]; } } } }
Int QP = Int( 4.2005 * log( lambda ) + 13.7122 + 0.5 );
main TAppEncTop::encode TEncTop::encode(先initRCGOP再compressGOP) TEncGOP::compressGOP TEncRCPic::calAverageLambda Double TEncRCPic::calAverageLambda() { Double totalLambdas = 0.0; Int numTotalLCUs = 0; Int i; for ( i=0; i<m_numberOfLCU; i++ ) { if ( m_LCUs[i].m_lambda > 0.01 ) { totalLambdas += log( m_LCUs[i].m_lambda ); numTotalLCUs++; } } Double avgLambda; if( numTotalLCUs == 0 ) { avgLambda = -1.0; } else { avgLambda = pow( 2.7183, totalLambdas / numTotalLCUs ); } return avgLambda; }
main TAppEncTop::encode TEncTop::encode(先initRCGOP再compressGOP) TEncGOP::compressGOP TEncRCPic:: calAverageQP Double TEncRCPic::calAverageQP() { Int totalQPs = 0; Int numTotalLCUs = 0; Int i; for ( i=0; i<m_numberOfLCU; i++ ) { if ( m_LCUs[i].m_QP > 0 ) { totalQPs += m_LCUs[i].m_QP; numTotalLCUs++; } } Double avgQP = 0.0; if ( numTotalLCUs == 0 ) { avgQP = g_RCInvalidQPValue; } else { avgQP = ((Double)totalQPs) / ((Double)numTotalLCUs); } return avgQP; }
更新过程按下式进行
Double estLambda = alpha * pow( bpp, beta );
main TAppEncTop::encode TEncTop::encode(先initRCGOP再compressGOP) TEncGOP::compressGOP TEncRCPic::updateAfterLCU Void TEncRCPic::updateAfterLCU( 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; 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; 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( 0.05, 20.0, alpha ); beta = Clip3( -3.0, -0.1, 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, 1.0, lnbpp ); beta += m_encRCSeq->getBetaUpdate() * ( log( inputLambda ) - log( calLambda ) ) * lnbpp; alpha = Clip3( 0.05, 20.0, alpha ); beta = Clip3( -3.0, -0.1, beta ); TRCParameter rcPara; rcPara.m_alpha = alpha; rcPara.m_beta = beta; m_encRCSeq->setLCUPara( m_frameLevel, LCUIdx, rcPara ); } 以及 main TAppEncTop::encode TEncTop::encode(先initRCGOP再compressGOP) TEncGOP::compressGOP TEncRCPic:: updateAfterPicture Void TEncRCPic::updateAfterPicture( Int actualHeaderBits, Int actualTotalBits, Double averageQP, Double averageLambda, Double effectivePercentage ) { m_picActualHeaderBits = actualHeaderBits; m_picActualBits = actualTotalBits; if ( averageQP > 0.0 ) { m_picQP = Int( averageQP + 0.5 ); } else { m_picQP = g_RCInvalidQPValue; } m_picLambda = averageLambda; for ( Int i=0; i<m_numberOfLCU; i++ ) { m_totalMAD += m_LCUs[i].m_MAD; } Double alpha = m_encRCSeq->getPicPara( m_frameLevel ).m_alpha; Double beta = m_encRCSeq->getPicPara( m_frameLevel ).m_beta; // update parameters 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 || effectivePercentage < 0.05 ) { alpha *= ( 1.0 - m_encRCSeq->getAlphaUpdate() / 2.0 ); beta *= ( 1.0 - m_encRCSeq->getBetaUpdate() / 2.0 ); alpha = Clip3( 0.05, 20.0, alpha ); beta = Clip3( -3.0, -0.1, 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, 1.0, lnbpp ); beta += m_encRCSeq->getBetaUpdate() * ( log( inputLambda ) - log( calLambda ) ) * lnbpp; alpha = Clip3( 0.05, 20.0, alpha ); beta = Clip3( -3.0, -0.1, beta ); TRCParameter rcPara; rcPara.m_alpha = alpha; rcPara.m_beta = beta; m_encRCSeq->setPicPara( m_frameLevel, rcPara ); }
Main TAppEncTop::encode TAppEncTop::xCreateLib TEncTop::create TEncRateCtrl::init TEncRCSeq::create 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; 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 { m_alphaUpdate = 0.1; m_betaUpdate = 0.05; }
当然,α和β也是有范围限定的。α 的值限定在 [0.05, 20] 而 β 的值限定在 [?3.0, ?0.1].
alpha = Clip3( 0.05, 20.0, alpha ); beta = Clip3( -3.0, -0.1, beta );
main TAppEncTop::encode TEncTop::encode(先initRCGOP再compressGOP) TEncGOP::compressGOP Double TEncRCPic::estimatePicLambda( list<TEncRCPic*>& listPreviousPictures ) { 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 = alpha * pow( bpp, beta ); 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; } } 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; return estLambda; } Int TEncRCPic::estimatePicQP( Double lambda, list<TEncRCPic*>& listPreviousPictures ) { Int QP = Int( 4.2005 * log( lambda ) + 13.7122 + 0.5 ); 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; } } 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; }
在LCU层有
Double TEncRCPic::getLCUEstLambda( Double bpp ) { Int LCUIdx = getLCUCoded(); Double alpha; Double beta; if ( m_encRCSeq->getUseLCUSeparateModel() ) { alpha = m_encRCSeq->getLCUPara( m_frameLevel, LCUIdx ).m_alpha; beta = m_encRCSeq->getLCUPara( m_frameLevel, LCUIdx ).m_beta; } else { 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 clip Double clipPicLambda = m_estPicLambda; //for Lambda clip, LCU level clip Double 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; } } 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; } 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 clip Int clipNeighbourQP = g_RCInvalidQPValue; #if L0033_RC_BUGFIX for ( int i=LCUIdx - 1; i>=0; i-- ) #else for ( int i=LCUIdx; i>=0; i-- ) #endif { if ( (getLCU(i)).m_QP > g_RCInvalidQPValue ) { clipNeighbourQP = getLCU(i).m_QP; break; } } if ( clipNeighbourQP > g_RCInvalidQPValue ) { estQP = Clip3( clipNeighbourQP - 1, clipNeighbourQP + 1, estQP ); } estQP = Clip3( clipPicQP - 2, clipPicQP + 2, estQP ); return estQP; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
HEVC码率控制算法研究与HM相应代码分析(三)——算法及代码分析
原文地址:http://blog.csdn.net/nonmarking/article/details/47357657