【HEVC学习与研究】37、HM编码器的基本结构2:帧内编码部分的代码骨架

第31篇博文大体介绍了HEVC参考代码HM10的编码器结构,但仅仅停留在compressCU()以上的层次,并未对具体编码的结构做深入解析。在此篇博文中我们依据对视频YUV序列的帧内编码的流程,重新梳理HM编码器的代码结构。

1、编码器的上层结构:

主要包括:

  1. 入口点函数main()【创建cTAppEncTop类,解析输入的配置函数,设定时间相关的参数
  2. cTAppEncTop.encode()【对编码器所使用的几个对象进行初始化,分配YUV数据缓存,循环读取YUV文件
  3. m_cTEncTop.encode(...)【调用m_cGOPEncoder.compressGOP()实现对一个GOP的实际编码
  4. m_cGOPEncoder.compressGOP()【调用initGOP设置GOP的参数;调用m_pcSliceEncoder->initEncSlice(),利用SPS和PPS中的信息创建编码的slice对象;调用m_pcSliceEncoder->compressSlice ( pcPic )对一个slice进行编码。
  5. m_pcSliceEncoder->compressSlice ( pcPic )【设置编码slice的参数,对slice的每一个cu进行处理
  6. TEncCu::compressCU()【编码一个CU】

以上各个函数分别在前一个函数中调用,形成类似一个反向的call stack的结构。

2、对一个CU进行编码的过程:

TEncCu::compressCU()函数的实现如下:

Void TEncCu::compressCU( TComDataCU*& rpcCU )
{
  // initialize CU data
  m_ppcBestCU[0]->initCU( rpcCU->getPic(), rpcCU->getAddr() );
  m_ppcTempCU[0]->initCU( rpcCU->getPic(), rpcCU->getAddr() );

#if RATE_CONTROL_LAMBDA_DOMAIN
  m_addSADDepth      = 0;
  m_LCUPredictionSAD = 0;
  m_temporalSAD      = 0;
#endif

  // analysis of CU
  xCompressCU( m_ppcBestCU[0], m_ppcTempCU[0], 0 );

#if ADAPTIVE_QP_SELECTION
  if( m_pcEncCfg->getUseAdaptQpSelect() )
  {
    if(rpcCU->getSlice()->getSliceType()!=I_SLICE) //IIII
    {
      xLcuCollectARLStats( rpcCU);
    }
  }
#endif
}

该函数的核心方法为xCompressCU( m_ppcBestCU[0], m_ppcTempCU[0], 0 )。该函数实现了编码一个CU的多种功能,首先判断尝试进行帧间预测,然后尝试进行帧内预测,另外还包括了PCM模式的编码以及一个CU的进一步划分等。这里主要关心的是intra模式,实现的代码为:

if ( !bEarlySkip )
{
// speedup for inter frames
        if( rpcBestCU->getSlice()->getSliceType() == I_SLICE || 
        rpcBestCU->getCbf( 0, TEXT_LUMA     ) != 0   ||
        rpcBestCU->getCbf( 0, TEXT_CHROMA_U ) != 0   ||
        rpcBestCU->getCbf( 0, TEXT_CHROMA_V ) != 0     ) // avoid very complex intra if it is unlikely
        {
        xCheckRDCostIntra( rpcBestCU, rpcTempCU, SIZE_2Nx2N );
           rpcTempCU->initEstData( uiDepth, iQP );
            if( uiDepth == g_uiMaxCUDepth - g_uiAddCUDepth )
            {
                      if( rpcTempCU->getWidth(0) > ( 1 << rpcTempCU->getSlice()->getSPS()->getQuadtreeTULog2MinSize() ) )
               {
                xCheckRDCostIntra( rpcBestCU, rpcTempCU, SIZE_NxN   );
                rpcTempCU->initEstData( uiDepth, iQP );
                }
        }
}
}

该函数调用的xCheckRDCostIntra实现了帧内预测的具体过程:
Void TEncCu::xCheckRDCostIntra( TComDataCU*& rpcBestCU, TComDataCU*& rpcTempCU, PartSize eSize )
{
  UInt uiDepth = rpcTempCU->getDepth( 0 );
  
  rpcTempCU->setSkipFlagSubParts( false, 0, uiDepth );

  rpcTempCU->setPartSizeSubParts( eSize, 0, uiDepth );
  rpcTempCU->setPredModeSubParts( MODE_INTRA, 0, uiDepth );
  rpcTempCU->setCUTransquantBypassSubParts( m_pcEncCfg->getCUTransquantBypassFlagValue(), 0, uiDepth );
  
  Bool bSeparateLumaChroma = true; // choose estimation mode
  UInt uiPreCalcDistC      = 0;
  if( !bSeparateLumaChroma )
  {
    m_pcPredSearch->preestChromaPredMode( rpcTempCU, m_ppcOrigYuv[uiDepth], m_ppcPredYuvTemp[uiDepth] );
  }
  m_pcPredSearch  ->estIntraPredQT      ( rpcTempCU, m_ppcOrigYuv[uiDepth], m_ppcPredYuvTemp[uiDepth], m_ppcResiYuvTemp[uiDepth], m_ppcRecoYuvTemp[uiDepth], uiPreCalcDistC, bSeparateLumaChroma );

  m_ppcRecoYuvTemp[uiDepth]->copyToPicLuma(rpcTempCU->getPic()->getPicYuvRec(), rpcTempCU->getAddr(), rpcTempCU->getZorderIdxInCU() );
  
  m_pcPredSearch  ->estIntraPredChromaQT( rpcTempCU, m_ppcOrigYuv[uiDepth], m_ppcPredYuvTemp[uiDepth], m_ppcResiYuvTemp[uiDepth], m_ppcRecoYuvTemp[uiDepth], uiPreCalcDistC );
  
  m_pcEntropyCoder->resetBits();
  if ( rpcTempCU->getSlice()->getPPS()->getTransquantBypassEnableFlag())
  {
    m_pcEntropyCoder->encodeCUTransquantBypassFlag( rpcTempCU, 0,          true );
  }
  m_pcEntropyCoder->encodeSkipFlag ( rpcTempCU, 0,          true );
  m_pcEntropyCoder->encodePredMode( rpcTempCU, 0,          true );
  m_pcEntropyCoder->encodePartSize( rpcTempCU, 0, uiDepth, true );
  m_pcEntropyCoder->encodePredInfo( rpcTempCU, 0,          true );
  m_pcEntropyCoder->encodeIPCMInfo(rpcTempCU, 0, true );

  // Encode Coefficients
  Bool bCodeDQP = getdQPFlag();
  m_pcEntropyCoder->encodeCoeff( rpcTempCU, 0, uiDepth, rpcTempCU->getWidth (0), rpcTempCU->getHeight(0), bCodeDQP );
  setdQPFlag( bCodeDQP );
  
  if( m_bUseSBACRD ) m_pcRDGoOnSbacCoder->store(m_pppcRDSbacCoder[uiDepth][CI_TEMP_BEST]);
  
  rpcTempCU->getTotalBits() = m_pcEntropyCoder->getNumberOfWrittenBits();
  if(m_pcEncCfg->getUseSBACRD())
  {
    rpcTempCU->getTotalBins() = ((TEncBinCABAC *)((TEncSbac*)m_pcEntropyCoder->m_pcEntropyCoderIf)->getEncBinIf())->getBinsCoded();
  }
  rpcTempCU->getTotalCost() = m_pcRdCost->calcRdCost( rpcTempCU->getTotalBits(), rpcTempCU->getTotalDistortion() );
  
  xCheckDQP( rpcTempCU );
  xCheckBestMode(rpcBestCU, rpcTempCU, uiDepth);
}
该函数调用了estIntraPredQT和estIntraPredChromaQT分别实现亮度分量和色度分量的帧内估计,二者大同小异。这里重点观察亮度分量。estIntraPredQT执行了遍历各种intra模式的操作,调用了pcCU->getPattern()->initAdiPattern、predIntraLumaAng等函数。详情可见编号第3336的博文。

上一篇:HTTP协议


下一篇:java线程学习4——线程同步之同步代码块