HM编码器代码阅读(10)——片的编码

入口函数TEncSlice::compressSlice
这个函数主要是设置一些参数和初始化一些东西,然后对片中的每一个LCU调用initCU(初始化CU)和compressCU(对CU编码)和encodeCU(对CU进行熵编码,目的是选择最优参数)。
TEncSlice::compressSlice函数的详解:
(1)计算当前slice的开始CU和结束CU
(2)初始化Sbac编码器
(3)再把slice的熵编码器设置为Sbac编码器
(4)重置熵编码器(主要是上下文的重置)
(5)取得二值化编码器
(6)取得比特计数器
(7)遍历slice中的每一个LCU
     ①初始化LCU
     ②调用compressCU,用于求最优模式
     ③调用encodeCU,进行熵编码
// 对条带编码
Void TEncSlice::compressSlice( TComPic*& rpcPic )
{
	// CU的地址
	UInt  uiCUAddr;
	// CU的开始地址
	UInt   uiStartCUAddr;
	// CU的边界地址
	UInt   uiBoundingCUAddr;
	// 条带中当前的比特数量是0
	rpcPic->getSlice(getSliceIdx())->setSliceSegmentBits(0);
	TEncBinCABAC* pppcRDSbacCoder = NULL;

	// 当前的条带
	TComSlice* pcSlice            = rpcPic->getSlice(getSliceIdx());

	// 得到CU(LCU)的开始和结束地址	
	xDetermineStartAndBoundingCUAddr ( uiStartCUAddr, uiBoundingCUAddr, rpcPic, false );
	//uiStartCUAddr = 0, uiBoundingCUAddr = 2304

	// initialize cost values
    // 图像总的比特数
	m_uiPicTotalBits  = 0;
    // 率失真代价
	m_dPicRdCost      = 0;
    // 帧的distortion(失真)
	m_uiPicDist       = 0;

	// set entropy coder
	// 初始化熵编码器
	m_pcSbacCoder->init( m_pcBinCABAC );
	// 设置熵编码器
	m_pcEntropyCoder->setEntropyCoder   ( m_pcSbacCoder, pcSlice );
	// 重置熵编码
	// //!< 主要进行上下文模型的初始化,codILow和codIRange的初始化等
	m_pcEntropyCoder->resetEntropy      ();
	// 加载熵编码器SBAC
	m_pppcRDSbacCoder[0][CI_CURR_BEST]->load(m_pcSbacCoder);
	pppcRDSbacCoder = (TEncBinCABAC *) m_pppcRDSbacCoder[0][CI_CURR_BEST]->getEncBinIf();
	pppcRDSbacCoder->setBinCountingEnableFlag( false );
	pppcRDSbacCoder->setBinsCoded( 0 );

	//------------------------------------------------------------------------------
	//  Weighted Prediction parameters estimation.
	//------------------------------------------------------------------------------
	// calculate AC/DC values for current picture
	// 不进入,因为没有使用波前前向预测
	if( pcSlice->getPPS()->getUseWP() || pcSlice->getPPS()->getWPBiPred() )
	{
		xCalcACDCParamSlice(pcSlice);
	}

	Bool bWp_explicit = (pcSlice->getSliceType()==P_SLICE && pcSlice->getPPS()->getUseWP()) || (pcSlice->getSliceType()==B_SLICE && pcSlice->getPPS()->getWPBiPred());

	// 不进入
	if ( bWp_explicit )
	{
		//------------------------------------------------------------------------------
		//  Weighted Prediction implemented at Slice level. SliceMode=2 is not supported yet.
		//------------------------------------------------------------------------------
		if ( pcSlice->getSliceMode()==2 || pcSlice->getSliceSegmentMode()==2 )
		{
			printf("Weighted Prediction is not supported with slice mode determined by max number of bins.\n"); exit(0);
		}

		xEstimateWPParamSlice( pcSlice );
		pcSlice->initWpScaling();

		// check WP on/off
		xCheckWPEnable( pcSlice );
	}

	// 自适应量化步长
#if ADAPTIVE_QP_SELECTION
	// 不进入
	if( m_pcCfg->getUseAdaptQpSelect() )
	{
		m_pcTrQuant->clearSliceARLCnt();
		if(pcSlice->getSliceType()!=I_SLICE)
		{
			Int qpBase = pcSlice->getSliceQpBase();
			pcSlice->setSliceQp(qpBase + m_pcTrQuant->getQpDelta(qpBase));
		}
	}
#endif

	// 获取编码器的配置
	TEncTop* pcEncTop = (TEncTop*) m_pcCfg;

	// 从配置中取得sbac编码器
	TEncSbac**** ppppcRDSbacCoders    = pcEncTop->getRDSbacCoders();
	// 从配置中获取比特计数器
	TComBitCounter* pcBitCounters     = pcEncTop->getBitCounters();

	// 子流的数量
	Int  iNumSubstreams = 1;
	// 穿过区块的个数
	UInt uiTilesAcross  = 0;

	iNumSubstreams = pcSlice->getPPS()->getNumSubstreams();//1
	uiTilesAcross = rpcPic->getPicSym()->getNumColumnsMinus1()+1;//1
	delete[] m_pcBufferSbacCoders;
	delete[] m_pcBufferBinCoderCABACs;
	m_pcBufferSbacCoders     = new TEncSbac    [uiTilesAcross];
	m_pcBufferBinCoderCABACs = new TEncBinCABAC[uiTilesAcross];
	for (Int ui = 0; ui < uiTilesAcross; ui++)
	{
		m_pcBufferSbacCoders[ui].init( &m_pcBufferBinCoderCABACs[ui] );
	}
	for (UInt ui = 0; ui < uiTilesAcross; ui++)
	{
		m_pcBufferSbacCoders[ui].load(m_pppcRDSbacCoder[0][CI_CURR_BEST]);  //init. state
	}

	for ( UInt ui = 0 ; ui < iNumSubstreams ; ui++ ) //init all sbac coders for RD optimization
	{
		ppppcRDSbacCoders[ui][0][CI_CURR_BEST]->load(m_pppcRDSbacCoder[0][CI_CURR_BEST]);
	}

	delete[] m_pcBufferLowLatSbacCoders;
	delete[] m_pcBufferLowLatBinCoderCABACs;
	m_pcBufferLowLatSbacCoders     = new TEncSbac    [uiTilesAcross];
	m_pcBufferLowLatBinCoderCABACs = new TEncBinCABAC[uiTilesAcross];
	for (Int ui = 0; ui < uiTilesAcross; ui++)
	{
		m_pcBufferLowLatSbacCoders[ui].init( &m_pcBufferLowLatBinCoderCABACs[ui] );
	}
	for (UInt ui = 0; ui < uiTilesAcross; ui++)
		m_pcBufferLowLatSbacCoders[ui].load(m_pppcRDSbacCoder[0][CI_CURR_BEST]);  //init. state

	// 获取一帧图像中纵向可以存放多少个CU(目前是3个)
	// 同理垂直方向上也有可以存放3个,因此,一个slice中有3*3=9个LCU
	UInt uiWidthInLCUs  = rpcPic->getPicSym()->getFrameWidthInCU();		// 3
	//UInt uiHeightInLCUs = rpcPic->getPicSym()->getFrameHeightInCU();
	UInt uiCol=0, uiLin=0, uiSubStrm=0;
	UInt uiTileCol      = 0;
	UInt uiTileStartLCU = 0;
	UInt uiTileLCUX     = 0;
	// 禁用了依赖性条带片段
	Bool depSliceSegmentsEnabled = pcSlice->getPPS()->getDependentSliceSegmentsEnabledFlag(); // false
	uiCUAddr = rpcPic->getPicSym()->getCUOrderMap( uiStartCUAddr /rpcPic->getNumPartInCU());//0
	uiTileStartLCU = rpcPic->getPicSym()->getTComTile(rpcPic->getPicSym()->getTileIdxMap(uiCUAddr))->getFirstCUAddr();//0

    // 是否启用了条带片段功能
	if( depSliceSegmentsEnabled )
	{
		if((pcSlice->getSliceSegmentCurStartCUAddr()!= pcSlice->getSliceCurStartCUAddr())&&(uiCUAddr != uiTileStartLCU))
		{
			if( m_pcCfg->getWaveFrontsynchro() )
			{
				uiTileCol = rpcPic->getPicSym()->getTileIdxMap(uiCUAddr) % (rpcPic->getPicSym()->getNumColumnsMinus1()+1);
				m_pcBufferSbacCoders[uiTileCol].loadContexts( CTXMem[1] );
				Int iNumSubstreamsPerTile = iNumSubstreams/rpcPic->getPicSym()->getNumTiles();
				uiCUAddr = rpcPic->getPicSym()->getCUOrderMap( uiStartCUAddr /rpcPic->getNumPartInCU()); 
				uiLin     = uiCUAddr / uiWidthInLCUs;
				uiSubStrm = rpcPic->getPicSym()->getTileIdxMap(rpcPic->getPicSym()->getCUOrderMap(uiCUAddr))*iNumSubstreamsPerTile
					+ uiLin%iNumSubstreamsPerTile;
				if ( (uiCUAddr%uiWidthInLCUs+1) >= uiWidthInLCUs  )
				{
					uiTileLCUX = uiTileStartLCU % uiWidthInLCUs;
					uiCol     = uiCUAddr % uiWidthInLCUs;
					if(uiCol==uiTileStartLCU)
					{
						CTXMem[0]->loadContexts(m_pcSbacCoder);
					}
				}
			}
			m_pppcRDSbacCoder[0][CI_CURR_BEST]->loadContexts( CTXMem[0] );
			ppppcRDSbacCoders[uiSubStrm][0][CI_CURR_BEST]->loadContexts( CTXMem[0] );
		}
		else
		{
			if(m_pcCfg->getWaveFrontsynchro())
			{
				CTXMem[1]->loadContexts(m_pcSbacCoder);
			}
			CTXMem[0]->loadContexts(m_pcSbacCoder);
		}
	}

	// for every CU in slice
	// 处理条带中的每一个CU

	int nLCUofSlice = 0;
	// 编码CU的顺序
	UInt uiEncCUOrder;

	// rpcPic->getNumPartInCU()获取一个LCU分裂的个数,即它内部包含多少个CU
	for( uiEncCUOrder = uiStartCUAddr/rpcPic->getNumPartInCU();
		uiEncCUOrder < (uiBoundingCUAddr+(rpcPic->getNumPartInCU()-1))/rpcPic->getNumPartInCU();
		uiCUAddr = rpcPic->getPicSym()->getCUOrderMap(++uiEncCUOrder) )
	{
		// initialize CU encoder
		// 根据CU的地址取得CU(LCU) 
		TComDataCU*& pcCU = rpcPic->getCU( uiCUAddr );

		// 初始化CU
		pcCU->initCU( rpcPic, uiCUAddr );

		// HM15.0中似乎没有用到区块
		// inherit from TR if necessary, select substream to use.
		uiTileCol = rpcPic->getPicSym()->getTileIdxMap(uiCUAddr) % (rpcPic->getPicSym()->getNumColumnsMinus1()+1); // what column of tiles are we in?  // 0
		uiTileStartLCU = rpcPic->getPicSym()->getTComTile(rpcPic->getPicSym()->getTileIdxMap(uiCUAddr))->getFirstCUAddr(); // 0
		uiTileLCUX = uiTileStartLCU % uiWidthInLCUs; // 0
		//UInt uiSliceStartLCU = pcSlice->getSliceCurStartCUAddr();
		uiCol     = uiCUAddr % uiWidthInLCUs; // 0
		uiLin     = uiCUAddr / uiWidthInLCUs; // 0

		// 子比特流数量是1,所以没有进去
		if (pcSlice->getPPS()->getNumSubstreams() > 1)
		{
			// independent tiles => substreams are "per tile".  iNumSubstreams has already been multiplied.
			Int iNumSubstreamsPerTile = iNumSubstreams/rpcPic->getPicSym()->getNumTiles();
			uiSubStrm = rpcPic->getPicSym()->getTileIdxMap(uiCUAddr)*iNumSubstreamsPerTile
				+ uiLin%iNumSubstreamsPerTile;
		}
		else
		{
			// dependent tiles => substreams are "per frame".
			uiSubStrm = uiLin % iNumSubstreams; //0
		}

        //
		if ( ((pcSlice->getPPS()->getNumSubstreams() > 1) || depSliceSegmentsEnabled ) && (uiCol == uiTileLCUX) && m_pcCfg->getWaveFrontsynchro())
		{
			// We'll sync if the TR is available.
			TComDataCU *pcCUUp = pcCU->getCUAbove();
			UInt uiWidthInCU = rpcPic->getFrameWidthInCU();
			UInt uiMaxParts = 1<<(pcSlice->getSPS()->getMaxCUDepth()<<1);
			TComDataCU *pcCUTR = NULL;
			if ( pcCUUp && ((uiCUAddr%uiWidthInCU+1) < uiWidthInCU)  )
			{
				pcCUTR = rpcPic->getCU( uiCUAddr - uiWidthInCU + 1 );
			}
			if ( ((pcCUTR==NULL) || (pcCUTR->getSlice()==NULL) ||
				(pcCUTR->getSCUAddr()+uiMaxParts-1 < pcSlice->getSliceCurStartCUAddr()) ||
				((rpcPic->getPicSym()->getTileIdxMap( pcCUTR->getAddr() ) != rpcPic->getPicSym()->getTileIdxMap(uiCUAddr)))
				)
				)
			{
				// TR not available.
			}
			else
			{
				// TR is available, we use it.
				ppppcRDSbacCoders[uiSubStrm][0][CI_CURR_BEST]->loadContexts( &m_pcBufferSbacCoders[uiTileCol] );
			}
		}
		m_pppcRDSbacCoder[0][CI_CURR_BEST]->load( ppppcRDSbacCoders[uiSubStrm][0][CI_CURR_BEST] ); //this load is used to simplify the code

		// reset the entropy coder
		// 重置熵编码器
		if( uiCUAddr == rpcPic->getPicSym()->getTComTile(rpcPic->getPicSym()->getTileIdxMap(uiCUAddr))->getFirstCUAddr() &&                                   // must be first CU of tile
			uiCUAddr!=0 &&                                                                                                                                    // cannot be first CU of picture
			uiCUAddr!=rpcPic->getPicSym()->getPicSCUAddr(rpcPic->getSlice(rpcPic->getCurrSliceIdx())->getSliceSegmentCurStartCUAddr())/rpcPic->getNumPartInCU() &&
			uiCUAddr!=rpcPic->getPicSym()->getPicSCUAddr(rpcPic->getSlice(rpcPic->getCurrSliceIdx())->getSliceCurStartCUAddr())/rpcPic->getNumPartInCU())     // cannot be first CU of slice
		{
			SliceType sliceType = pcSlice->getSliceType();
			if (!pcSlice->isIntra() && pcSlice->getPPS()->getCabacInitPresentFlag() && pcSlice->getPPS()->getEncCABACTableIdx()!=I_SLICE)
			{
				sliceType = (SliceType) pcSlice->getPPS()->getEncCABACTableIdx();
			}
			m_pcEntropyCoder->updateContextTables ( sliceType, pcSlice->getSliceQp(), false );
			m_pcEntropyCoder->setEntropyCoder     ( m_pppcRDSbacCoder[0][CI_CURR_BEST], pcSlice );
			m_pcEntropyCoder->updateContextTables ( sliceType, pcSlice->getSliceQp() );
			m_pcEntropyCoder->setEntropyCoder     ( m_pcSbacCoder, pcSlice );
		}

		// set go-on entropy coder
		m_pcEntropyCoder->setEntropyCoder ( m_pcRDGoOnSbacCoder, pcSlice );
		m_pcEntropyCoder->setBitstream( &pcBitCounters[uiSubStrm] );

		((TEncBinCABAC*)m_pcRDGoOnSbacCoder->getEncBinIf())->setBinCountingEnableFlag(true);

		Double oldLambda = m_pcRdCost->getLambda();//57.9。。。

		// 没有使用码率控制
		if ( m_pcCfg->getUseRateCtrl() )
		{
			Int estQP        = pcSlice->getSliceQp();
			Double estLambda = -1.0;
			Double bpp       = -1.0;

			if ( ( rpcPic->getSlice( 0 )->getSliceType() == I_SLICE && m_pcCfg->getForceIntraQP() ) || !m_pcCfg->getLCULevelRC() )
			{
				estQP = pcSlice->getSliceQp();
			}
			else
			{
				bpp = m_pcRateCtrl->getRCPic()->getLCUTargetBpp(pcSlice->getSliceType());
				if ( rpcPic->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()->getQpBDOffsetY(), MAX_QP, estQP );

				m_pcRdCost->setLambda(estLambda);
#if RDOQ_CHROMA_LAMBDA
				// set lambda for RDOQ
				Double weight=m_pcRdCost->getChromaWeight();
				const Double lambdaArray[3] = { estLambda, (estLambda / weight), (estLambda / weight) };
				m_pcTrQuant->setLambdas( lambdaArray );
#else
				m_pcTrQuant->setLambda( estLambda );
#endif
			}

			m_pcRateCtrl->setRCQP( estQP );
#if ADAPTIVE_QP_SELECTION
			pcCU->getSlice()->setSliceQpBase( estQP );
#endif
		}

		// run CU encoder
		// 对CU进行编码(压缩)
		// 帧内预测,帧间预测编码还有变换编码
		// 这里很重要
		// 编码单元编码
		// 最重要的部分!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        // 注意这个只是尝试进行,然后选出最优熵编码方案,下面的encodeCU才是真正进行熵编码的地方
		m_pcCuEncoder->compressCU( pcCU );
		// 最重要的部分!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

		// restore entropy coder to an initial stage
		// 熵编码器设置为Sbac
		m_pcEntropyCoder->setEntropyCoder ( m_pppcRDSbacCoder[0][CI_CURR_BEST], pcSlice );
		// 设置需要写入的比特流
		m_pcEntropyCoder->setBitstream( &pcBitCounters[uiSubStrm] );
		m_pcCuEncoder->setBitCounter( &pcBitCounters[uiSubStrm] );
		// 比特计数器(用于统计熵编码器写入到比特流中的比特数)
		m_pcBitCounter = &pcBitCounters[uiSubStrm];
		pppcRDSbacCoder->setBinCountingEnableFlag( true );
		m_pcBitCounter->resetBits();
		pppcRDSbacCoder->setBinsCoded( 0 );

		// 对CU进编码
		// 这里是真正的进行熵编码!!!!
		// 重要!!!!!!!!!!!!!
		m_pcCuEncoder->encodeCU( pcCU );
		// 重要!!!!!!!!!!!!!

		pppcRDSbacCoder->setBinCountingEnableFlag( false );

		// 这两个判断,是为了判断该CU是否为条带中的最后一个CU,如果是则跳出循环
		if (m_pcCfg->getSliceMode()==FIXED_NUMBER_OF_BYTES && ( ( pcSlice->getSliceBits() + m_pcEntropyCoder->getNumberOfWrittenBits() ) ) > m_pcCfg->getSliceArgument()<<3)
		{
			pcSlice->setNextSlice( true );
			break;
		}
		if (m_pcCfg->getSliceSegmentMode()==FIXED_NUMBER_OF_BYTES && pcSlice->getSliceSegmentBits()+m_pcEntropyCoder->getNumberOfWrittenBits() > (m_pcCfg->getSliceSegmentArgument() << 3) &&pcSlice->getSliceCurEndCUAddr()!=pcSlice->getSliceSegmentCurEndCUAddr())
		{
			pcSlice->setNextSliceSegment( true );
			break;
		}

		ppppcRDSbacCoders[uiSubStrm][0][CI_CURR_BEST]->load( m_pppcRDSbacCoder[0][CI_CURR_BEST] );
		//Store probabilties of second LCU in line into buffer
		if ( ( uiCol == uiTileLCUX+1) && (depSliceSegmentsEnabled || (pcSlice->getPPS()->getNumSubstreams() > 1)) && m_pcCfg->getWaveFrontsynchro())
		{
			m_pcBufferSbacCoders[uiTileCol].loadContexts(ppppcRDSbacCoders[uiSubStrm][0][CI_CURR_BEST]);
		}

		// 没有使用码率控制
		if ( m_pcCfg->getUseRateCtrl() )
		{

			Int actualQP        = g_RCInvalidQPValue;
			Double actualLambda = m_pcRdCost->getLambda();
			Int actualBits      = pcCU->getTotalBits();
			Int numberOfEffectivePixels    = 0;
			for ( Int idx = 0; idx < rpcPic->getNumPartInCU(); idx++ )
			{
				if ( pcCU->getPredictionMode( idx ) != MODE_NONE && ( !pcCU->isSkipped( idx ) ) )
				{
					numberOfEffectivePixels = numberOfEffectivePixels + 16;
					break;
				}
			}

			if ( numberOfEffectivePixels == 0 )
			{
				actualQP = g_RCInvalidQPValue;
			}
			else
			{
				actualQP = pcCU->getQP( 0 );
			}
			m_pcRdCost->setLambda(oldLambda);

			m_pcRateCtrl->getRCPic()->updateAfterLCU( m_pcRateCtrl->getRCPic()->getLCUCoded(), actualBits, actualQP, actualLambda,
				pcCU->getSlice()->getSliceType() == I_SLICE ? 0 : m_pcCfg->getLCULevelRC() );
		}

		// 计算总的比特数
		m_uiPicTotalBits += pcCU->getTotalBits();
		// 计算运行代价
		m_dPicRdCost     += pcCU->getTotalCost();
		// 计算失真率
		m_uiPicDist      += pcCU->getTotalDistortion();
	} // for end

	if ((pcSlice->getPPS()->getNumSubstreams() > 1) && !depSliceSegmentsEnabled)
	{
		pcSlice->setNextSlice( true );
	}
	if(m_pcCfg->getSliceMode()==FIXED_NUMBER_OF_BYTES || m_pcCfg->getSliceSegmentMode()==FIXED_NUMBER_OF_BYTES)
	{
		if(pcSlice->getSliceCurEndCUAddr()<=pcSlice->getSliceSegmentCurEndCUAddr())
		{
			pcSlice->setNextSlice( true );
		}
		else
		{
			pcSlice->setNextSliceSegment( true );
		}
	}
	if( depSliceSegmentsEnabled )
	{
		if (m_pcCfg->getWaveFrontsynchro())
		{
			CTXMem[1]->loadContexts( &m_pcBufferSbacCoders[uiTileCol] );//ctx 2.LCU
		}
		CTXMem[0]->loadContexts( m_pppcRDSbacCoder[0][CI_CURR_BEST] );//ctx end of dep.slice
	}
	// 存储WP参数
	xRestoreWPparam( pcSlice );
}


你可能感兴趣的:(C++,编码,h.265,HEVC)