HM编码器代码阅读(24)——视频数据的熵编码

入口函数TEncSlice::encodeSlice。
在处理完VPS,SPS,PPS和片头信息之后就开始对片的数据(不仅是数据还有一些其他的参数和控制信息)进行编码了。
encodeSlice函数就是熵编码的重头戏了。处理过程如下:
(1)先初始化熵编码器,然后设置CABAC熵编码器为当前的熵编码器。
(2)然后就是加载各种熵编码器,各种初始化,各种加载上下文信息。
(3)遍历片中的每一个LCU,进行下面的处理:
     1)设置比特流(即熵编码之后要把数据写到哪里去)
     2)重置熵编码器,调用updateContextTables更新上下文信息
     3)如果使用了SAO,那么调用encodeSAOBlkParam对SAO的参数进行编码
     4)调用encodeCU对LCU进行熵编码(我们注意到前面compressSlice中也会调用该函数,在compressSlice中的调用只是为了选择最优的模式)。

Void TEncSlice::encodeSlice   ( TComPic*& rpcPic, TComOutputBitstream* pcSubstreams )
{
	UInt       uiCUAddr;
	UInt       uiStartCUAddr;
	UInt       uiBoundingCUAddr;

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

	uiStartCUAddr=pcSlice->getSliceSegmentCurStartCUAddr();
	uiBoundingCUAddr=pcSlice->getSliceSegmentCurEndCUAddr();
	// choose entropy coder

	// 设置熵编码器CABAC
	{
		m_pcSbacCoder->init( (TEncBinIf*)m_pcBinCABAC );
		m_pcEntropyCoder->setEntropyCoder ( m_pcSbacCoder, pcSlice );
	}

	m_pcCuEncoder->setBitCounter( NULL );
	m_pcBitCounter = NULL;
	// Appropriate substream bitstream is switched later.
	// for every CU
#if ENC_DEC_TRACE
	g_bJustDoIt = g_bEncDecTraceEnable;
#endif
	DTRACE_CABAC_VL( g_nSymbolCounter++ );
	DTRACE_CABAC_T( "\tPOC: " );
	DTRACE_CABAC_V( rpcPic->getPOC() );
	DTRACE_CABAC_T( "\n" );
#if ENC_DEC_TRACE
	g_bJustDoIt = g_bEncDecTraceDisable;
#endif

	TEncTop* pcEncTop = (TEncTop*) m_pcCfg;
	TEncSbac* pcSbacCoders = pcEncTop->getSbacCoders(); //coder for each substream
	Int iNumSubstreams = pcSlice->getPPS()->getNumSubstreams();
	UInt uiBitsOriginallyInSubstreams = 0;
	{
		UInt uiTilesAcross = rpcPic->getPicSym()->getNumColumnsMinus1()+1;
		for (UInt ui = 0; ui < uiTilesAcross; ui++)
		{
			m_pcBufferSbacCoders[ui].load(m_pcSbacCoder); //init. state
		}

		for (Int iSubstrmIdx=0; iSubstrmIdx < iNumSubstreams; iSubstrmIdx++)
		{
			uiBitsOriginallyInSubstreams += pcSubstreams[iSubstrmIdx].getNumberOfWrittenBits();
		}

		for (UInt ui = 0; ui < uiTilesAcross; ui++)
		{
			m_pcBufferLowLatSbacCoders[ui].load(m_pcSbacCoder);  //init. state
		}
	}

	UInt uiWidthInLCUs  = rpcPic->getPicSym()->getFrameWidthInCU();
	UInt uiCol=0, uiLin=0, uiSubStrm=0;
	UInt uiTileCol      = 0;
	UInt uiTileStartLCU = 0;
	UInt uiTileLCUX     = 0;

	// 是否使用了条带片段(按照默认的配置,没有使用)
	Bool depSliceSegmentsEnabled = pcSlice->getPPS()->getDependentSliceSegmentsEnabledFlag();
	uiCUAddr = rpcPic->getPicSym()->getCUOrderMap( uiStartCUAddr /rpcPic->getNumPartInCU());  /* for tiles, uiStartCUAddr is NOT the real raster scan address, it is actually
																							  an encoding order index, so we need to convert the index (uiStartCUAddr)
																							  into the real raster scan address (uiCUAddr) via the CUOrderMap */
	uiTileStartLCU = rpcPic->getPicSym()->getTComTile(rpcPic->getPicSym()->getTileIdxMap(uiCUAddr))->getFirstCUAddr();
	
	// 按照默认的设置,没有使用
	if( depSliceSegmentsEnabled )
	{
		if( pcSlice->isNextSlice()||
			uiCUAddr == rpcPic->getPicSym()->getTComTile(rpcPic->getPicSym()->getTileIdxMap(uiCUAddr))->getFirstCUAddr())
		{
			if(m_pcCfg->getWaveFrontsynchro())
			{
				CTXMem[1]->loadContexts(m_pcSbacCoder);
			}
			CTXMem[0]->loadContexts(m_pcSbacCoder);
		}
		else
		{
			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();
				uiLin     = uiCUAddr / uiWidthInLCUs;
				uiSubStrm = rpcPic->getPicSym()->getTileIdxMap(rpcPic->getPicSym()->getCUOrderMap( uiCUAddr))*iNumSubstreamsPerTile
					+ uiLin%iNumSubstreamsPerTile;
				if ( (uiCUAddr%uiWidthInLCUs+1) >= uiWidthInLCUs  )
				{
					uiCol     = uiCUAddr % uiWidthInLCUs;
					uiTileLCUX = uiTileStartLCU % uiWidthInLCUs;
					if(uiCol==uiTileLCUX)
					{
						CTXMem[0]->loadContexts(m_pcSbacCoder);
					}
				}
			}
			pcSbacCoders[uiSubStrm].loadContexts( CTXMem[0] );
		}
	}

	UInt uiEncCUOrder;

	// 遍历条带中的每一个CU
	for( uiEncCUOrder = uiStartCUAddr /rpcPic->getNumPartInCU();
		uiEncCUOrder < (uiBoundingCUAddr+rpcPic->getNumPartInCU()-1)/rpcPic->getNumPartInCU();
		uiCUAddr = rpcPic->getPicSym()->getCUOrderMap(++uiEncCUOrder) )
	{
		uiTileCol = rpcPic->getPicSym()->getTileIdxMap(uiCUAddr) % (rpcPic->getPicSym()->getNumColumnsMinus1()+1); // what column of tiles are we in?
		uiTileStartLCU = rpcPic->getPicSym()->getTComTile(rpcPic->getPicSym()->getTileIdxMap(uiCUAddr))->getFirstCUAddr();
		uiTileLCUX = uiTileStartLCU % uiWidthInLCUs;
		//UInt uiSliceStartLCU = pcSlice->getSliceCurStartCUAddr();
		uiCol     = uiCUAddr % uiWidthInLCUs;
		uiLin     = uiCUAddr / uiWidthInLCUs;
		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;
		}

		m_pcEntropyCoder->setBitstream( &pcSubstreams[uiSubStrm] );
		// Synchronize cabac probabilities with upper-right LCU if it's available and we're at the start of a line.
		if (((pcSlice->getPPS()->getNumSubstreams() > 1) || depSliceSegmentsEnabled) && (uiCol == uiTileLCUX) && m_pcCfg->getWaveFrontsynchro())
		{
			// We'll sync if the TR is available.
			TComDataCU *pcCUUp = rpcPic->getCU( uiCUAddr )->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 ( (true/*bEnforceSliceRestriction*/ &&
				((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.
				pcSbacCoders[uiSubStrm].loadContexts( &m_pcBufferSbacCoders[uiTileCol] );
			}
		}
		m_pcSbacCoder->load(&pcSbacCoders[uiSubStrm]);  //this load is used to simplify the code (avoid to change all the call to m_pcSbacCoder)

		// 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
		{
			{
				// We're crossing into another tile, tiles are independent.
				// When tiles are independent, we have "substreams per tile".  Each substream has already been terminated, and we no longer
				// have to perform it here.
				if (pcSlice->getPPS()->getNumSubstreams() > 1)
				{
					; // do nothing.
				}
				else
				{
					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() );
					// Byte-alignment in slice_data() when new tile
					pcSubstreams[uiSubStrm].writeByteAlignment();
				}
			}
			{
				UInt numStartCodeEmulations = pcSubstreams[uiSubStrm].countStartCodeEmulations();
				UInt uiAccumulatedSubstreamLength = 0;
				for (Int iSubstrmIdx=0; iSubstrmIdx < iNumSubstreams; iSubstrmIdx++)
				{
					uiAccumulatedSubstreamLength += pcSubstreams[iSubstrmIdx].getNumberOfWrittenBits();
				}
				// add bits coded in previous dependent slices + bits coded so far
				// add number of emulation prevention byte count in the tile
				pcSlice->addTileLocation( ((pcSlice->getTileOffstForMultES() + uiAccumulatedSubstreamLength - uiBitsOriginallyInSubstreams) >> 3) + numStartCodeEmulations );
			}
		}

		TComDataCU*& pcCU = rpcPic->getCU( uiCUAddr );    
		if ( pcSlice->getSPS()->getUseSAO() )
		{
			if (pcSlice->getSaoEnabledFlag()||pcSlice->getSaoEnabledFlagChroma())
			{
				SAOBlkParam& saoblkParam = (rpcPic->getPicSym()->getSAOBlkParam())[uiCUAddr];
				Bool sliceEnabled[NUM_SAO_COMPONENTS];
				sliceEnabled[SAO_Y] = pcSlice->getSaoEnabledFlag();
				sliceEnabled[SAO_Cb]= sliceEnabled[SAO_Cr]= pcSlice->getSaoEnabledFlagChroma();

				Bool leftMergeAvail = false;
				Bool aboveMergeAvail= false;
				//merge left condition
				Int rx = (uiCUAddr % uiWidthInLCUs);
				if(rx > 0)
				{
					leftMergeAvail = rpcPic->getSAOMergeAvailability(uiCUAddr, uiCUAddr-1);
				}

				//merge up condition
				Int ry = (uiCUAddr / uiWidthInLCUs);
				if(ry > 0)
				{
					aboveMergeAvail = rpcPic->getSAOMergeAvailability(uiCUAddr, uiCUAddr-uiWidthInLCUs);
				}

				m_pcEntropyCoder->encodeSAOBlkParam(saoblkParam,sliceEnabled, leftMergeAvail, aboveMergeAvail);
			}
		}

#if ENC_DEC_TRACE
		g_bJustDoIt = g_bEncDecTraceEnable;
#endif
		if ( (m_pcCfg->getSliceMode()!=0 || m_pcCfg->getSliceSegmentMode()!=0) &&
			uiCUAddr == rpcPic->getPicSym()->getCUOrderMap((uiBoundingCUAddr+rpcPic->getNumPartInCU()-1)/rpcPic->getNumPartInCU()-1) )
		{
			// 和compressSlice一样调用了encodeCU函数,但是实现的功能已经不一样了
			// 因为compressSlice主要是进行帧内(帧间)最优模式的选择,还有变换、量化等功能
			// 而在这里,由于前面的几个步骤都已经进行完毕,所以,这里是单纯的进行熵编码
			m_pcCuEncoder->encodeCU( pcCU );
		}
		else
		{
			m_pcCuEncoder->encodeCU( pcCU );
		}
#if ENC_DEC_TRACE
		g_bJustDoIt = g_bEncDecTraceDisable;
#endif    
		pcSbacCoders[uiSubStrm].load(m_pcSbacCoder);   //load back status of the entropy coder after encoding the LCU into relevant bitstream entropy coder
		//Store probabilties of second LCU in line into buffer
		if ( (depSliceSegmentsEnabled || (pcSlice->getPPS()->getNumSubstreams() > 1)) && (uiCol == uiTileLCUX+1) && m_pcCfg->getWaveFrontsynchro())
		{
			m_pcBufferSbacCoders[uiTileCol].loadContexts( &pcSbacCoders[uiSubStrm] );
		}
	}
	if( depSliceSegmentsEnabled )
	{
		if (m_pcCfg->getWaveFrontsynchro())
		{
			CTXMem[1]->loadContexts( &m_pcBufferSbacCoders[uiTileCol] );//ctx 2.LCU
		}
		CTXMem[0]->loadContexts( m_pcSbacCoder );//ctx end of dep.slice
	}
#if ADAPTIVE_QP_SELECTION
	if( m_pcCfg->getUseAdaptQpSelect() )
	{
		m_pcTrQuant->storeSliceQpNext(pcSlice);
	}
#endif
	if (pcSlice->getPPS()->getCabacInitPresentFlag())
	{
		if  (pcSlice->getPPS()->getDependentSliceSegmentsEnabledFlag())
		{
			pcSlice->getPPS()->setEncCABACTableIdx( pcSlice->getSliceType() );
		}
		else
		{
			// 决定CABAC的初始化索引
			m_pcEntropyCoder->determineCabacInitIdx();
		}
	}
}

TEncCu::encodeCU(或TEncCu::xEncodeCU)函数的执行步骤(用的仍然是CABAC熵编码器):
(1)调用encodeSplitFlag对分割标志进行编码(最终调用TEncSbac::codeSplitFlag)
(2)如果当前CU会继续向下进行分割,那么递归调用xEncodeCU,直到到达最底层
(3)调用encodeCUTransquantBypassFlag,对变换量化跳过标志进行编码(最终调用的是TEncSbac::codeCUTransquantBypassFlag)
(4)如果不是帧内预测,那么编码skip标志(最后调用的是TEncSbac::codeSkipFlag)
(5)如果当前的CU是帧间skip模式的,那么调用encodeMergeIndex(最后调用TEncSbac::codeMergeIndex),对merge模式选出的索引进行编码,然后结束编码,返回(因为它的MV等信息是预测出来的,而不是计算出来的,因此到此结束处理)。
(6)调用encodePredMode,对预测的模式进行编码(最后调用TEncSbac::codePredMode)
(7)调用encodePartSize,对分割的尺寸进行编码(最后调用TEncSbac::codePartSize)
(8)如果是帧内预测,并且划分的方式是SIZE_2Nx2N,那么调用encodeIPCMInfo对IPCM的信息进行编码(最后调用TEncSbac::codeIPCMInfo),如果确实使用了IPCM,那么结束编码,返回!
(9)调用encodePredInfo,对预测的信息进行编码
(10)调用encodeCoeff,对系数进行编码(最后调用TEncSbac::codeCoeffNxN)

(11)调用finishCU,结束熵编码

Void TEncCu::xEncodeCU( TComDataCU* pcCU, UInt uiAbsPartIdx, UInt uiDepth )
{
	TComPic* pcPic = pcCU->getPic();

	Bool bBoundary = false;
	UInt uiLPelX   = pcCU->getCUPelX() + g_auiRasterToPelX[ g_auiZscanToRaster[uiAbsPartIdx] ];
	UInt uiRPelX   = uiLPelX + (g_uiMaxCUWidth>>uiDepth)  - 1;
	UInt uiTPelY   = pcCU->getCUPelY() + g_auiRasterToPelY[ g_auiZscanToRaster[uiAbsPartIdx] ];
	UInt uiBPelY   = uiTPelY + (g_uiMaxCUHeight>>uiDepth) - 1;

	TComSlice * pcSlice = pcCU->getPic()->getSlice(pcCU->getPic()->getCurrSliceIdx());
	// If slice start is within this cu...
	Bool bSliceStart = pcSlice->getSliceSegmentCurStartCUAddr() > pcPic->getPicSym()->getInverseCUOrderMap(pcCU->getAddr())*pcCU->getPic()->getNumPartInCU()+uiAbsPartIdx && 
		pcSlice->getSliceSegmentCurStartCUAddr() < pcPic->getPicSym()->getInverseCUOrderMap(pcCU->getAddr())*pcCU->getPic()->getNumPartInCU()+uiAbsPartIdx+( pcPic->getNumPartInCU() >> (uiDepth<<1) );
	// We need to split, so don't try these modes.
	if(!bSliceStart&&( uiRPelX < pcSlice->getSPS()->getPicWidthInLumaSamples() ) && ( uiBPelY < pcSlice->getSPS()->getPicHeightInLumaSamples() ) )
	{
		// 对分割标志进行编码
		m_pcEntropyCoder->encodeSplitFlag( pcCU, uiAbsPartIdx, uiDepth );
	}
	else
	{
		bBoundary = true;
	}

	if( ( ( uiDepth < pcCU->getDepth( uiAbsPartIdx ) ) && ( uiDepth < (g_uiMaxCUDepth-g_uiAddCUDepth) ) ) || bBoundary )
	{
		UInt uiQNumParts = ( pcPic->getNumPartInCU() >> (uiDepth<<1) )>>2;
		if( (g_uiMaxCUWidth>>uiDepth) == pcCU->getSlice()->getPPS()->getMinCuDQPSize() && pcCU->getSlice()->getPPS()->getUseDQP())
		{
			setdQPFlag(true);
		}
		for ( UInt uiPartUnitIdx = 0; uiPartUnitIdx < 4; uiPartUnitIdx++, uiAbsPartIdx+=uiQNumParts )
		{
			uiLPelX   = pcCU->getCUPelX() + g_auiRasterToPelX[ g_auiZscanToRaster[uiAbsPartIdx] ];
			uiTPelY   = pcCU->getCUPelY() + g_auiRasterToPelY[ g_auiZscanToRaster[uiAbsPartIdx] ];
			Bool bInSlice = pcCU->getSCUAddr()+uiAbsPartIdx+uiQNumParts>pcSlice->getSliceSegmentCurStartCUAddr()&&pcCU->getSCUAddr()+uiAbsPartIdx<pcSlice->getSliceSegmentCurEndCUAddr();
			if(bInSlice&&( uiLPelX < pcSlice->getSPS()->getPicWidthInLumaSamples() ) && ( uiTPelY < pcSlice->getSPS()->getPicHeightInLumaSamples() ) )
			{
				// 递归调用,直到每一个slice中的每一个cu都处理完毕
				xEncodeCU( pcCU, uiAbsPartIdx, uiDepth+1 );
			}
		}
		return;
	}

	if( (g_uiMaxCUWidth>>uiDepth) >= pcCU->getSlice()->getPPS()->getMinCuDQPSize() && pcCU->getSlice()->getPPS()->getUseDQP())
	{
		setdQPFlag(true);
	}
	if (pcCU->getSlice()->getPPS()->getTransquantBypassEnableFlag())
	{
		// 对跳过变换过程的编制进行编码
		m_pcEntropyCoder->encodeCUTransquantBypassFlag( pcCU, uiAbsPartIdx );
	}
	if( !pcCU->getSlice()->isIntra() )
	{
		// 编码跳过标志
		m_pcEntropyCoder->encodeSkipFlag( pcCU, uiAbsPartIdx );
	}

	if( pcCU->isSkipped( uiAbsPartIdx ) )
	{
		// 编码合并索引
		m_pcEntropyCoder->encodeMergeIndex( pcCU, uiAbsPartIdx );
		// CU结束
		finishCU(pcCU,uiAbsPartIdx,uiDepth);
		return;
	}

	// 编码预测模式
	m_pcEntropyCoder->encodePredMode( pcCU, uiAbsPartIdx );

	// 对编码块的尺寸进行编码
	m_pcEntropyCoder->encodePartSize( pcCU, uiAbsPartIdx, uiDepth );

	if (pcCU->isIntra( uiAbsPartIdx ) && pcCU->getPartitionSize( uiAbsPartIdx ) == SIZE_2Nx2N )
	{
		// 编码IPCM的信息
		m_pcEntropyCoder->encodeIPCMInfo( pcCU, uiAbsPartIdx );

		if(pcCU->getIPCMFlag(uiAbsPartIdx))
		{
			// Encode slice finish
			finishCU(pcCU,uiAbsPartIdx,uiDepth);
			return;
		}
	}

	// 编码预测信息,很重要!!!!!!!!!!!!!!
	// prediction Info ( Intra : direction mode, Inter : Mv, reference idx )
	m_pcEntropyCoder->encodePredInfo( pcCU, uiAbsPartIdx );
	// 很重要!!!!!!!!!!!!!!!!!!!!!

	// Encode Coefficients
	Bool bCodeDQP = getdQPFlag();

	// 对系数进行编码,很重要!!!!!!!!!!!!!!!!!!!!!!!!!!
	m_pcEntropyCoder->encodeCoeff( pcCU, uiAbsPartIdx, uiDepth, pcCU->getWidth (uiAbsPartIdx), pcCU->getHeight(uiAbsPartIdx), bCodeDQP );
	// 很重要!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

	setdQPFlag( bCodeDQP );

	// --- write terminating bit ---
	finishCU(pcCU,uiAbsPartIdx,uiDepth);
}


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