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(); } } }
(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); }