HEVC代码学习34:compressSlice函数

来源:互联网 发布:淘宝订单号泄漏 编辑:程序博客网 时间:2024/05/11 01:30

compressSlice,是Slice层编码的入口函数,主要完成的功能就是Slice层编码参数的初始化,其中会调用compressCtu(其中会调用xCompressCU,对CU进行划分)和encodeCtu(其中对调用xEncodeCU,对CU进行编码),这两个函数只是入口函数,十分简单,在本文第二部分进行分析。

compressSlice

主要工作流程:
1.计算Slice的起始CTU和边界CTU。
2.初始化率失真参数:bit、RD cost、失真。
3.初始化Sbac熵编码器,根据当前Slice设置参数。
4.遍历当前Slice中的CTU,对CU进行划分和编码,计算bit、RD cost、失真。其中调用了重要函数compressCtu和encodeCtu。

推荐阅读http://blog.csdn.net/nb_vol_1/article/details/51151803,不过大神使用的HM版本较早,没有引入CTU,使用的是LCU。

代码分析:

/** \param pcPic   picture class *///Slice层编码Void TEncSlice::compressSlice( TComPic* pcPic, const Bool bCompressEntireSlice, const Bool bFastDeltaQP ){  // if bCompressEntireSlice is true, then the entire slice (not slice segment) is compressed,  //   effectively disabling the slice-segment-mode.  //起始CTU序号  UInt   startCtuTsAddr;      //CTU边界序号  UInt   boundingCtuTsAddr;  //当前Slice  TComSlice* const pcSlice            = pcPic->getSlice(getSliceIdx());  //将Slice中当前bit置零  pcSlice->setSliceSegmentBits(0);        //计算当前Slice中的起始CTU和边界CTU  xDetermineStartAndBoundingCtuTsAddr ( startCtuTsAddr, boundingCtuTsAddr, pcPic );       //bCompressEntireSlice默认为false  if (bCompressEntireSlice)       {    boundingCtuTsAddr = pcSlice->getSliceCurEndCtuTsAddr();    pcSlice->setSliceSegmentCurEndCtuTsAddr(boundingCtuTsAddr);  }  // initialize cost values - these are used by precompressSlice (they should be parameters).  //初始化总bit、RD cost和失真  m_uiPicTotalBits  = 0;  m_dPicRdCost      = 0; // NOTE: This is a write-only variable!  m_uiPicDist       = 0;  //初始化熵编码器  m_pcEntropyCoder->setEntropyCoder   ( m_pppcRDSbacCoder[0][CI_CURR_BEST] );  //根据当前Slice设置熵编码参数  m_pcEntropyCoder->resetEntropy      ( pcSlice );        //加载熵编码器SBAC  TEncBinCABAC* pRDSbacCoder = (TEncBinCABAC *) m_pppcRDSbacCoder[0][CI_CURR_BEST]->getEncBinIf();  pRDSbacCoder->setBinCountingEnableFlag( false );  pRDSbacCoder->setBinsCoded( 0 );  //bit计数  TComBitCounter  tempBitCounter;     //帧每行的CTU个数  const UInt      frameWidthInCtus = pcPic->getPicSym()->getFrameWidthInCtus();       //快速DeltaQp默认关闭  m_pcCuEncoder->setFastDeltaQp(bFastDeltaQP);        //------------------------------------------------------------------------------  //  Weighted Prediction parameters estimation.  //------------------------------------------------------------------------------  // calculate AC/DC values for current picture  //默认关闭  if( pcSlice->getPPS()->getUseWP() || pcSlice->getPPS()->getWPBiPred() )     {    xCalcACDCParamSlice(pcSlice);  }  const Bool bWp_explicit = (pcSlice->getSliceType()==P_SLICE && pcSlice->getPPS()->getUseWP()) || (pcSlice->getSliceType()==B_SLICE && pcSlice->getPPS()->getWPBiPred());  //bWp_explicit默认为false  if ( bWp_explicit )     {    //------------------------------------------------------------------------------    //  Weighted Prediction implemented at Slice level. SliceMode=2 is not supported yet.    //------------------------------------------------------------------------------    if ( pcSlice->getSliceMode()==FIXED_NUMBER_OF_BYTES || pcSlice->getSliceSegmentMode()==FIXED_NUMBER_OF_BYTES )    {      printf("Weighted Prediction is not supported with slice mode determined by max number of bins.\n"); exit(0);    }    xEstimateWPParamSlice( pcSlice, m_pcCfg->getWeightedPredictionMethod() );    pcSlice->initWpScaling(pcSlice->getSPS());    // check WP on/off    xCheckWPEnable( pcSlice );  }#if ADAPTIVE_QP_SELECTION     //自适应QP,默认关闭  if( m_pcCfg->getUseAdaptQpSelect() && !(pcSlice->getDependentSliceSegmentFlag()))       {    // TODO: this won't work with dependent slices: they do not have their own QP. Check fix to mask clause execution with && !(pcSlice->getDependentSliceSegmentFlag())    m_pcTrQuant->clearSliceARLCnt(); // TODO: this looks wrong for multiple slices - the results of all but the last slice will be cleared before they are used (all slices compressed, and then all slices encoded)    if(pcSlice->getSliceType()!=I_SLICE)    {      Int qpBase = pcSlice->getSliceQpBase();      pcSlice->setSliceQp(qpBase + m_pcTrQuant->getQpDelta(qpBase));    }  }#endif  // Adjust initial state if this is the start of a dependent slice.  //调整初始状态  {    //CTU地址    const UInt      ctuRsAddr               = pcPic->getPicSym()->getCtuTsToRsAddrMap( startCtuTsAddr);    //当前Tile序号    const UInt      currentTileIdx          = pcPic->getPicSym()->getTileIdxMap(ctuRsAddr);    //当前Tile    const TComTile *pCurrentTile            = pcPic->getPicSym()->getTComTile(currentTileIdx);    //Tile中第一个CTU的地址    const UInt      firstCtuRsAddrOfTile    = pCurrentTile->getFirstCtuRsAddr();    //独立Slice且非第一个Tile时启用    if( pcSlice->getDependentSliceSegmentFlag() && ctuRsAddr != firstCtuRsAddrOfTile )    {      // This will only occur if dependent slice-segments (m_entropyCodingSyncContextState=true) are being used.      if( pCurrentTile->getTileWidthInCtus() >= 2 || !m_pcCfg->getEntropyCodingSyncEnabledFlag() )      {        m_pppcRDSbacCoder[0][CI_CURR_BEST]->loadContexts( &m_lastSliceSegmentEndContextState );      }    }  }  // for every CTU in the slice segment (may terminate sooner if there is a byte limit on the slice-segment)  //遍历Slice中的每一个CTU,对CTU进行编码  for( UInt ctuTsAddr = startCtuTsAddr; ctuTsAddr < boundingCtuTsAddr; ++ctuTsAddr )  {    //CTU地址    const UInt ctuRsAddr = pcPic->getPicSym()->getCtuTsToRsAddrMap(ctuTsAddr);    // initialize CTU encoder    //当前CTU    TComDataCU* pCtu = pcPic->getCtu( ctuRsAddr );    //初始化CTU    pCtu->initCtu( pcPic, ctuRsAddr );    // update CABAC state    //第一个CTU的地址    const UInt firstCtuRsAddrOfTile = pcPic->getPicSym()->getTComTile(pcPic->getPicSym()->getTileIdxMap(ctuRsAddr))->getFirstCtuRsAddr();    //计算Tile的x坐标    const UInt tileXPosInCtus = firstCtuRsAddrOfTile % frameWidthInCtus;    //计算CTU的x坐标    const UInt ctuXPosInCtus  = ctuRsAddr % frameWidthInCtus;    //如果是Tile的第一个CTU    if (ctuRsAddr == firstCtuRsAddrOfTile)    {      //设置熵编码参数      m_pppcRDSbacCoder[0][CI_CURR_BEST]->resetEntropy(pcSlice);    }    else if ( ctuXPosInCtus == tileXPosInCtus && m_pcCfg->getEntropyCodingSyncEnabledFlag())    {      // reset and then update contexts to the state at the end of the top-right CTU (if within current slice and tile).      m_pppcRDSbacCoder[0][CI_CURR_BEST]->resetEntropy(pcSlice);      // Sync if the Top-Right is available.      TComDataCU *pCtuUp = pCtu->getCtuAbove();      if ( pCtuUp && ((ctuRsAddr%frameWidthInCtus+1) < frameWidthInCtus)  )      {        TComDataCU *pCtuTR = pcPic->getCtu( ctuRsAddr - frameWidthInCtus + 1 );        if ( pCtu->CUIsFromSameSliceAndTile(pCtuTR) )        {          // Top-Right is available, we use it.          m_pppcRDSbacCoder[0][CI_CURR_BEST]->loadContexts( &m_entropyCodingSyncContextState );        }      }    }    // set go-on entropy coder (used for all trial encodings - the cu encoder and encoder search also have a copy of the same pointer)    //设置熵编码器    m_pcEntropyCoder->setEntropyCoder ( m_pcRDGoOnSbacCoder );    m_pcEntropyCoder->setBitstream( &tempBitCounter );    tempBitCounter.resetBits();    m_pcRDGoOnSbacCoder->load( m_pppcRDSbacCoder[0][CI_CURR_BEST] ); // this copy is not strictly necessary here, but indicates that the GoOnSbacCoder                                                                     // is reset to a known state before every decision process.    ((TEncBinCABAC*)m_pcRDGoOnSbacCoder->getEncBinIf())->setBinCountingEnableFlag(true);    //当前lambda    Double oldLambda = m_pcRdCost->getLambda();    //码率控制时启用    if ( m_pcCfg->getUseRateCtrl() )    {      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() )      {        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 );#endif    }    // run CTU trial encoder    //CU划分    m_pcCuEncoder->compressCtu( pCtu );    // All CTU decisions have now been made. Restore entropy coder to an initial stage, ready to make a true encode,    // which will result in the state of the contexts being correct. It will also count up the number of bits coded,    // which is used if there is a limit of the number of bytes per slice-segment.    //设置熵编码器参数    m_pcEntropyCoder->setEntropyCoder ( m_pppcRDSbacCoder[0][CI_CURR_BEST] );    m_pcEntropyCoder->setBitstream( &tempBitCounter );    pRDSbacCoder->setBinCountingEnableFlag( true );    m_pppcRDSbacCoder[0][CI_CURR_BEST]->resetBits();    pRDSbacCoder->setBinsCoded( 0 );    // encode CTU and calculate the true bit counters.    //CTU编码    m_pcCuEncoder->encodeCtu( pCtu );    pRDSbacCoder->setBinCountingEnableFlag( false );    const Int numberOfWrittenBits = m_pcEntropyCoder->getNumberOfWrittenBits();    // Calculate if this CTU puts us over slice bit size.    // cannot terminate if current slice/slice-segment would be 0 Ctu in size,    const UInt validEndOfSliceCtuTsAddr = ctuTsAddr + (ctuTsAddr == startCtuTsAddr ? 1 : 0);    // Set slice end parameter    //设置Slice结束参数    if(pcSlice->getSliceMode()==FIXED_NUMBER_OF_BYTES && pcSlice->getSliceBits()+numberOfWrittenBits > (pcSlice->getSliceArgument()<<3))    {      pcSlice->setSliceSegmentCurEndCtuTsAddr(validEndOfSliceCtuTsAddr);      pcSlice->setSliceCurEndCtuTsAddr(validEndOfSliceCtuTsAddr);      boundingCtuTsAddr=validEndOfSliceCtuTsAddr;    }    else if((!bCompressEntireSlice) && pcSlice->getSliceSegmentMode()==FIXED_NUMBER_OF_BYTES && pcSlice->getSliceSegmentBits()+numberOfWrittenBits > (pcSlice->getSliceSegmentArgument()<<3))    {      pcSlice->setSliceSegmentCurEndCtuTsAddr(validEndOfSliceCtuTsAddr);      boundingCtuTsAddr=validEndOfSliceCtuTsAddr;    }    //如果当前CTU超过了边界CTU,跳出    if (boundingCtuTsAddr <= ctuTsAddr)    {      break;    }    //设置bit数    pcSlice->setSliceBits( (UInt)(pcSlice->getSliceBits() + numberOfWrittenBits) );    pcSlice->setSliceSegmentBits(pcSlice->getSliceSegmentBits()+numberOfWrittenBits);    // Store probabilities of second CTU in line into buffer - used only if wavefront-parallel-processing is enabled.    //默认关闭    if ( ctuXPosInCtus == tileXPosInCtus+1 && m_pcCfg->getEntropyCodingSyncEnabledFlag())    {      m_entropyCodingSyncContextState.loadContexts(m_pppcRDSbacCoder[0][CI_CURR_BEST]);    }    //码率控制时启用    if ( m_pcCfg->getUseRateCtrl() )    {      Int actualQP        = g_RCInvalidQPValue;      Double actualLambda = m_pcRdCost->getLambda();      Int actualBits      = pCtu->getTotalBits();      Int numberOfEffectivePixels    = 0;      for ( Int idx = 0; idx < pcPic->getNumPartitionsInCtu(); idx++ )      {        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() );    }    //计算总的bit、RD cost、失真    m_uiPicTotalBits += pCtu->getTotalBits();    m_dPicRdCost     += pCtu->getTotalCost();    m_uiPicDist      += pCtu->getTotalDistortion();  }  // store context state at the end of this slice-segment, in case the next slice is a dependent slice and continues using the CABAC contexts.  if( pcSlice->getPPS()->getDependentSliceSegmentsEnabledFlag() )  {    m_lastSliceSegmentEndContextState.loadContexts( m_pppcRDSbacCoder[0][CI_CURR_BEST] );//ctx end of dep.slice  }  // stop use of temporary bit counter object.  m_pppcRDSbacCoder[0][CI_CURR_BEST]->setBitstream(NULL);  m_pcRDGoOnSbacCoder->setBitstream(NULL); // stop use of tempBitCounter.  // TODO: optimise cabac_init during compress slice to improve multi-slice operation  //if (pcSlice->getPPS()->getCabacInitPresentFlag() && !pcSlice->getPPS()->getDependentSliceSegmentsEnabledFlag())  //{  //  m_encCABACTableIdx = m_pcEntropyCoder->determineCabacInitIdx();  //}  //else  //{  //  m_encCABACTableIdx = pcSlice->getSliceType();  //}}

compressCtu和encodeCtu

compressCtu是块划分的入口函数,主要包含两部分:初始化CTU和调用xCompressCU进行块划分。xCompressCU是非常重要的函数,详细介绍见:http://blog.csdn.net/lin453701006/article/details/72401400

代码分析如下:

/**  \param  pCtu pointer of CU data class */Void TEncCu::compressCtu( TComDataCU* pCtu ){  // initialize CU data  //将最优CU置为当前CTU  m_ppcBestCU[0]->initCtu( pCtu->getPic(), pCtu->getCtuRsAddr() );  //将当前CU置为当前CTU  m_ppcTempCU[0]->initCtu( pCtu->getPic(), pCtu->getCtuRsAddr() );  // analysis of CU  DEBUG_STRING_NEW(sDebug)  //递归进行块划分  xCompressCU( m_ppcBestCU[0], m_ppcTempCU[0], 0 DEBUG_STRING_PASS_INTO(sDebug) );  DEBUG_STRING_OUTPUT(std::cout, sDebug)#if ADAPTIVE_QP_SELECTION  if( m_pcEncCfg->getUseAdaptQpSelect() )  {    if(pCtu->getSlice()->getSliceType()!=I_SLICE) //IIII    {      xCtuCollectARLStats( pCtu );    }  }#endif}

encodeCtu是CTU编码的入口函数,主要包含两部分:根据情况设置QP相关Flag和调用xEncodeCU进行CU编码。xEncodeCU详细分析见http://blog.csdn.net/lin453701006/article/details/78688889。

代码分析如下:

/** \param  pCtu  pointer of CU data class */Void TEncCu::encodeCtu ( TComDataCU* pCtu ){  //当开启DQP时,置flag  if ( pCtu->getSlice()->getPPS()->getUseDQP() )  {    setdQPFlag(true);  }  //当开启色度QP自适应时,置flag  if ( pCtu->getSlice()->getUseChromaQpAdj() )  {    setCodeChromaQpAdjFlag(true);  }  // Encode CU data  //CU编码  xEncodeCU( pCtu, 0, 0 );}
原创粉丝点击