HEVC码率控制代码分析_获取h264 poc-程序员宅基地

技术标签: HM代码  视频编解码  

参考阅读https://blog.csdn.net/HEVC_CJL/article/details/10982699https://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 picture
  numberOfLevel++;    // non-reference picture

  Int* 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 1
    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;
      }

      if ( keepHierBits == 2 )
      {
    
        adaptiveBit = 1;
      }
    }
    // K0103 table 2
    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;
      }

      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判断,除非开启adaptiveBits
  if ( 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等于0
    m_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)TGOP
  Int 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 overflow
        if (estimatedCpbFullness - estimatedBits > (Int)(m_pcRateCtrl->getCpbSize()*0.9f))
        {
    
          estimatedBits = estimatedCpbFullness - (Int)(m_pcRateCtrl->getCpbSize()*0.9f);
        }

        estimatedCpbFullness -= m_pcRateCtrl->getBufferingRate();
        // prevent underflow
        if (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计算出lamda
      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
      {
    
        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 overflow
            if (estimatedCpbFullness - bits > (Int)(m_pcRateCtrl->getCpbSize()*0.9f))
            {
    
              bits = estimatedCpbFullness - (Int)(m_pcRateCtrl->getCpbSize()*0.9f);
            }

            estimatedCpbFullness -= m_pcRateCtrl->getBufferingRate();
            // prevent underflow
            if (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 weight
  for ( 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的QP
        estQP = 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 RDOQ
        const Double chromaLambda = estLambda / m_pcRdCost->getChromaWeight();
        const Double lambdaArray[MAX_NUM_COMPONENT] = {
     estLambda, chromaLambda, chromaLambda };
        m_pcTrQuant->setLambdas( lambdaArray );
#else
        m_pcTrQuant->setLambda( estLambda );
#endif
      }

      m_pcRateCtrl->setRCQP( estQP ); 
#if ADAPTIVE_QP_SELECTION
      pCtu->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_remainingCostIntra
    m_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 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;
    }
  }

  //!< 在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 clip
  Int 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--;
}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/cztl520/article/details/124525539

智能推荐

android 代码 截取屏幕,如何以编程方式在Android上截取屏幕截图?-程序员宅基地

文章浏览阅读346次。这是允许我的屏幕截图存储在SD卡上的代码,以后用于满足您的任何需求:首先,您需要添加适当的权限来保存文件:这是代码(在Activity中运行):privatevoidtakeScreenshot(){Datenow=newDate();android.text.format.DateFormat.format("yyyy-MM-dd_hh:mm:ss",now);try{//i..._屏幕部分截图android代码

注册表禁用计算机管理,Win7注册表编辑器被管理员禁用的解除方法-程序员宅基地

文章浏览阅读2.7k次。熟悉Win7系统的朋友都知道,注册表编辑器是更改Win7系统设置的好工具。在注册表中可以完成控制面板中不能修改的设置。但是有些用户打开注册表编辑器的时候却被提示,注册表编辑器已被管理员禁用,这是怎么回事呢?如何解除Win7对注册表编辑器的限制呢?一、Win7打开注册表的方法1 Win+R键打开Win7系统的运行,在运行数输入regedit点击确定。二、注册表被禁用解除方法Win7注册表编辑器被管理..._注册表编辑器禁用和解除原因

mysql innodb 源码_【InnoDB源码分析】Redo log-程序员宅基地

文章浏览阅读502次。【版本:mysql-8.0.12】1. Mini Transaction(mtr)InnoDB会将事务执行过程拆分为若干个Mini Transaction(mtr),每个mtr包含一系列如加锁,写数据,写redo,放锁等操作。举个例子:void btr_truncate(const dict_index_t *index) {... ...page_size_t page_size(space-&..._mysql innodb 源代码 解读

matlab背景设置_preferences: path: c:\users\administrator\appdata\-程序员宅基地

文章浏览阅读515次。打开C:\Users\Administrator\AppData\Roaming\MathWorks\MATLAB\R2015b\matlab.prf修改为#MATLAB Preferences#Sat Jul 11 15:54:49 CST 2020Editor.VariableHighlighting.Color=C-6931898EditorMRUSize=I8ReplaceSearchText19=SReplaceSearchText18=SReplaceSearchText17=S_preferences: path: c:\users\administrator\appdata\roaming\mathworks\matlab\c

谁说技术男不浪漫!90后程序员2天做出情绪...-程序员宅基地

文章浏览阅读75次。点击上方[全栈开发者社区]→右上角[...]→[设为星标]点击领取全栈资料:全栈资料9月1日,一则关于#程序员2天做出猫咪情绪识别软件#的话题登上微博热搜,参与阅读的人数达到了82..._程序员自研

解决SpringBoot整合redis下的键值序列化的问题_spring boot redis 查询序列化key-程序员宅基地

文章浏览阅读1.3k次。文章目录思考redis实现键值序列化自定义一个序列化转换器编写redis配置文件编写application.properties编写User类编写controller编写service测试思考为什么键值要序序列化呢?​ 不同平台之间的数据传输,深拷贝,浅拷贝,如果不采用序列化,很容易在传输过程中出现各种错误,无法正常使用Redis的序列化到底是什么?​ 简单的是说 就是 key ..._spring boot redis 查询序列化key

随便推点

JavaSE基础知识(十三)--Java的数组以及数组的初始化_int a1=a[rand.nextint(a.length)];-程序员宅基地

文章浏览阅读1.1k次。Java SE 是什么,包括哪些内容(十三)?本文内容参考自Java8标准数组是相同类型的,用一个标识符名称封装到一起的一个对象引用序列或基本数据类型序列,数组是通过方括号下标操作符[ ]来定义和使用的,要定义一个数组,只需要在类型后面加上一个[ ],并取一个合适的标识符名称即可。上面的内容总共体现在三个方面:⑴、数组内的数据类型必须是同一类型,也就是说在一个数组内不能同时存储不同数据类型..._int a1=a[rand.nextint(a.length)];

计算机专业2016高考录取分数线,华南师范大学计算机类专业2016年在广东理科高考录取最低分数线...-程序员宅基地

文章浏览阅读129次。类似问题答案华南师范大学计算机类专业2015年在广东理科高考录取最低分数线学校 地 区 专业 年份 批次 类型 分数 华南师范大学 广东 计算机类 2015 一批 理科 612 学校 地 区 专业 年份 批次 类型 分数 华南师范大学 广东 计算机类 2016 一批 理科 563 华南师范大学 广东 计算机类 2016 一批 理科 563 华南师范大学 广东 计算机类 2015 一批 理科 612..._华南师范大学广东2016各专业分数线

arm-linux-gnueabihf opencv,opencv3.4.9 + armv7 + arm-linux-gnueabihf交叉编译-程序员宅基地

文章浏览阅读249次。mac上编译 arm linux gnueabi交叉编译工具链toolchaincrosstool-ng 编译和安装 交叉编译工具下载: git clone [email protected]:secularbird/crosstool-ng.git 切换到mac编译分支 git ...Windows平台交叉编译Arm Linux平台的QT5&period;7库1.准备交叉编译环境 环境说..._gcc-arm-linux-gnueabihf armv7l

mysql数据库dao模式_mysql中DAO模式-程序员宅基地

文章浏览阅读591次。JDBC封装优点:隔离细节降低代码间耦合性提高代码可扩展性和维护性附注:DAO模式提供了访问关系型数据系统所需操作的接口,将数据访问和业务逻辑分开,对上层提供面向对象的数据访问接口.DAO模式实现两层分离:代码间分工明确,数据访问层代码不影响业务逻辑层代码,这也符合单一职能原则,降低了耦合度,提高了代码的可复用性。。隔离了不同的数据库的实现,采用面向接口编程,如果底层数据变化了,如mysql变成了..._mysqldao

Android对数据按照时间排序,Android实现数据按照时间排序-程序员宅基地

文章浏览阅读748次。经常遇见一个列表,两个接口的情况,两个接口属于两个不同的表数据,那么数据拼接回来之后,并不是按照时间排序的,看起来就相当混乱,所以记录一下如何对数据按照时间排序。步骤一:格式化日期public static Date stringToDate(String dateString) {ParsePosition position = new ParsePosition(0);SimpleDateFo..._安卓代码实现聊天记录根据时间排序

华硕固件默认ip,新路由3 newifi d2刷机刷华硕固件教程-程序员宅基地

文章浏览阅读4.9k次。新路由3默认的功能非常的少,所以不刷个好固件简直就是浪费硬件性能,恢复出厂后默认的路由器IP就是192.168.99.1。准备下面几个工具和固件上面工具准备好以后,下面开始刷机:一. 开启固件 SSH1、开启路由器,进入管理界面 (假设路由器 IP 地址是 192.168.99.1)2、 在浏览器中输入 http://192.168.99.1/newifi/ifiwen_hss.html 并进入3..._路由器刷华硕固件之后默认ip