精华内容
下载资源
问答
  • HEVC函数入门(16)——Slice编码

    千次阅读 2017-08-01 22:05:07
    这篇文章很东西还没搞懂,所以先转载放在这。 入口函数TEncSlice::compressSlice 这个函数主要是设置一些参数和初始化一些东西,然后对片中的每一个LCU调用initCU(初始化CU)和compressCU(对CU编码)和...

    本文转载整理自:http://blog.csdn.net/NB_vol_1/article/details/51152578
    这篇文章很多东西还没搞懂,所以先转载放在这。
    入口函数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 );  
    }  
    展开全文
  • h264多slice

    2017-05-25 22:20:00
    1, h264编码以macroblock为最小单位(简称MB),个连续的MB组成一个slice,每个slice编码输出一个NALU 2, 划分slice的方式可以按照固定个数MB的方式; 也可以以对MB编码后累计的字节数为依据进行划分。在图像上...

    1, h264编码以macroblock为最小单位(简称MB),多个连续的MB组成一个slice,每个slice编码输出一个NALU

    2, 划分slice的方式可以按照固定个数MB的方式; 也可以以对MB编码后累计的字节数为依据进行划分。在图像上体现为一个连续区域块,区域块的长度(以MB为单位)可以变化。

    3, 每个slice的编码过程是相互独立的。分多slice之后,可以并发地对多个slice进行编码,加快编码速度。同时分多slice之后,后续若slice内部出错,错误可以被限制在该slice内部,而不扩散到slice外部,在解码时花屏的面积就比较小。

    4, 分多slice之后,对应若在解码器端出错,那么若实施纠错机制,对应所需要处理的区域就可以较小,减少消耗。

    5,分多slice之后,由于每个slice都需要自己专有的元信息,并且编码过程中slice内部可参考的信息就减少了很多,最终编码输出的码流体积会相对变大。

    6,当然一幅图像编码可以放在一个slice里进行;具体需要根据业务场景进行综合选择slice模式,比如对带宽的要求、对容错要求等。

    转载于:https://www.cnblogs.com/lanyuliuyun/p/6906256.html

    展开全文
  • HEVC编码结构:Slice和Tile

    千次阅读 2016-10-07 11:32:05
    HEVC编码结构中的Slice、Tile结构概念及两者关系

    1、Slice片段层

    一幅图像可以被划分为一个或多个片或称为条带(Slice),每个片的数据编码都是独立的。

    如下图,一幅图像被划分为N个Slice,Slice成条带形。在编码时,每一个Slice中的CTU按光栅扫描顺序进行编码。


    Slice头信息无法通过前一个Slice的头信息推断得到,这就要求Slice不能跨过它的边界来进行帧内或帧间预测,但环路滤波器可以跨越Slice进行滤波。使用Slice的主要目的是当数据丢失后能再次保证解码同步。


    根据编码类型,Slice可以分为:

    (1)I Slice:该Slice中所有CU的编码过程都使用帧内预测。

    (2)P Slice:在I Slice的基础上,该Slice中的CU还可以使用帧间预测,每个预测块(PB)使用至多一个运动补偿预测信息。P Slice只使用图像参考列表list0。

    (3)B Slice:在P Slice的基础上,B Slice中的CU也可以使用帧间预测,但是每个PB可以使用至多两个运动补偿预测信息。B Slice可以使用图像参考列表list 0和list 1。


    一个独立的Slice可以进一步划分为若干个条带片段Slice segment(SS),包括一个独立SS和若干个依赖SS。

    如图Slice以独立SS作为开始,一个SS包含整数个CTU(至少一个)。预测过程不可以跨越Slice边界,但是可以跨越依赖SS边界,一个Slice中的SS之间可以相互参考。




    2、Tile单元

    在HEVC中一幅图像可以划分为若干个Tile(这是相比AVC的优化改进),即从从水平和垂直方向将图像分割为若干个矩形区域,把这些矩形区域称为Tile。

    下图是一种划分的示例,划分的Tile并不要求均匀分布,整幅图像被划分为9个Tile,每个Tile都是矩形。通常每个Tile包含的CTU数据近似相等。


    每个Tile包含整数个CTU,其可以独立编码,在编码时,每一个Tile包含的CTU按照扫描顺序进行编码。划分Tile的主要目的是增强并行处理能力而不引入新的错误扩散。



    3、Slice与Tile的关系

    一幅图像可以被划分为若干个Slice,也可以划分为若干个Tile,两者划分的目的都是为了进行独立编码。某些Slice中可以包含多个Tile,同样某些Tile中也可以包含多个Slice。Tile包含的CTU个数和Slice中的CTU个数互不影响。


    不同点:

    划分方式:Tile为矩形,Slice为条带形。

    组成结构:Slice由一系列的SS组成,一个SS由一系列的CTU组成。而Tile直接由一系列CTU组成。


    Slice/SS和Tile要遵循的一些基本原则,每个Slice/SS和Tile之间至少满足以下两个条件之一:

    (1)一个Slice/SS中的所有CTU属于同一个Tile。

    例如下图中一幅图像的每一个Slice的所有CTU都属于同一个Tile。


    (2)一个Tile中所有CTU属于同一个Slice/SS。

    如下图,一幅图像在垂直方向被划分为三个Tile,这三个Tile中各自的所有CTU都属于同一个Slice。



    展开全文
  • 如下随便打开一个cfg文件,Slices和Tiles部分是设置多slicetile的一些参数 SliceMode和SliceArgument是两个配套参数 1.当SliceMode等于0时关闭帧内slice的划分 2.当SliceMode等于1时代表每个slice内最多包含...

    如下随便打开一个cfg文件,Slices和Tiles部分是设置多slice和多tile的一些参数
    在这里插入图片描述

    SliceMode和SliceArgument是两个配套参数

    1.当SliceMode等于0时关闭帧内slice的划分

    2.当SliceMode等于1时代表每个slice内最多包含多少个CTU,数目由SliceArgument控制

    3.当SliceMode等于2时代表每个slice最多有多少字节,字节数目由SliceArgument控制

    4.当SliceMode等于3时代表每个slice内最多包含多少个tile,数目由SliceArgument控制

    TileUniformSpacing用来控制行和列tile的数目

    1.等于0时代表每一行和每一列的tile数不一定,每个tile列数和tile行数分别由TileColumnWidthArray和TileRowHeightArray分别设置

    2.等于1时行和列均匀分布,行列数由TileColumnWidthArray和TileRowHeightArray设置

    NumTileRowsMinus1 行tile数减1

    NumTileColumnsMinus1 列tile数减1

    TileColumnWidthArray :用逗号或者空格做分隔符的一个列表表示每一列有多少个tile,列表中的第一个值代表最左边的列有几个tile

    TileRowHeightArray:用逗号或者空格做分隔符的一个列表表示每一行有多少个tile,列表中的第一个值代表最上边的行有几个tile

    展开全文
  • 多slice码流

    2020-07-30 20:43:43
    有的推流端出于编码性能的考虑,会选择通过切片的方式来编码,这样一个frame的数据就可能被切片分散于个nalu中(哥伦布编码),但是videotoolbox解码又必须是整个frame合并到一个nalu中解码,暂时不知道合并算法,...
  • h264编码概述五(slice定义)

    千次阅读 2018-11-16 17:55:41
    h264编码可以将一幅图片分割成若干和SliceSlice承载固定个数个宏块。将一幅图片分割成若干Slice的目的是,为限制误码的扩散和传输。在H264编码协议中定义,当前帧的当前Slice片内宏块不允许参考其他Slice的宏块。 ...
  • H.264视频编码标准的特点出发,提出了基于Slice级别的H.264视频编码并行算法,该算法不仅能够保证节点间的负载平衡,减少 各节点间数据的依赖关系,还充分利用了已有的计算能力。最后给出了在曙光3000上的实验结果...
  • 编码 1表示使用CABAC,0表示CALVC num_slice_groups_minus1 -> 分片数量 weighted_pred_flag -> 在P/SP Slice中加权预测的方法 weighted_bipred_idc -> 在B Slice中加权预测的方法 pic_init_qp_minus26/...
  • 2005 - 基于Slice的H_264并行视频编码算法
  • 这里推断是rtmp封装问题导致了花屏,下面首先需要对于这种一帧H264视频中包含Slice的情况,应当如何封装。 2.与ffmpeg推流进行对比 这里们尝试使用ffmpeg来推一段多slice的H264码流,ffplay拉流播放
  • openh264限制slice/nal分片大小导致的编码数据错误最近在使用openh264进行编码的时候发现,一旦限制了slice/nal分片的大小,编码输出的数据就会出现错误。最后确定这个是openh264的bug。这里讲一下这个问题的原因和...
  • 关于多slice数据硬解码

    千次阅读 2015-12-18 19:08:04
    有的推流端处于编码性能的考虑,会选择通过切片的方式来编码,这样一个frame的数据就可能被切片分散于个nalu中(哥伦布编码),但是videotoolbox有解码又必须是整个frame合并到一个nalu中解码,暂时没找到直接硬解码...
  • x264源代码简单分析:x264_slice_write() 来自:https://blog.csdn.net/leixiaohua1020/article/details/45536607     H264 编码简介 https://blog.csdn.net/mydear_11000/article/details/49990637   H...
  • 2009年的一个x264的vfw版本,开源x264vfw版本已经不更新了,这里加入的线程支持,slice大小控制在1000字节左右(这样打RTP包方便) 1c61eab4aef4.rar:原始当到的x264版本 win32_pthreads.2.rar:win32下的pthread...
  • H.264的SliceSlice类型

    2017-12-22 19:41:05
    一个视频图像可编码成一个或更个条带,每个条带包含整数个宏块(MB),即每个条带至少一个MB,最多时每个条带包含整个图像的宏块。总之,一幅图像中每个条带的宏块数不一定固定。设条带的目的是为了限制误码的扩散...
  • ICE之Slice语言

    2019-04-07 17:22:46
    Slice 语言定义 文档中对于slice的定义 在这里,我们提供Slice语言。 Slice(Specification Language for Ice)是一种将对象接口与其实现分离的基本抽象机制。Slice在客户端和服务器之间建立一个描述应用程序...
  • 引用网友的问答:我找到0x000001 NAL的开头了,请问如何确定slice head的位置,继而得出slice_type呢?Nal unit后紧跟的就是slice head吗?标准里的循环让人看得有点迷茫,求大神指导Q1024440466 h264 slice...
  • x264线程编码

    2018-11-01 23:33:10
    slice并行把一帧划分为个矩形slice,在这slice之间并行处理,是一种非延时性的并行模式,多slice会稍微降低编码性能。frame并行是同时开启编码,x264在N个frame并行的时候需要集齐...
  • ffmpeg线程编码原理

    千次阅读 2018-12-17 15:34:00
    分片在编码中叫sliceslice的意义是把一帧分成个独立的编码单元,为什么需要slice呢,我感觉有两个方面,1.线程,为了编码更快;2.传输为了防止错误蔓延到其他区域,比如一帧图像分成上下两个分slice,每个...
  • Hi3559A中,默认单帧最多...如果用默认参数,对于单帧多slice和频繁gop的情况,会出现花屏和卡顿,解决方法是修改 sample_comm_vdec.c 在SAMPLE_COMM_VDEC_Start函数中,增加VDEC_PRTCL_PARAM_S ProtocolParam的设
  • H264关于一帧完整帧被分切成Slice时的合并思路

    千次阅读 热门讨论 2018-06-22 16:10:31
    在对H264进行编解码分析时,碰到一个完整视频帧被切分成Slice用于网络媒体传输的情况。而且接收端进行解码时需要一个完整帧(FFmpeg解码不需要拼接,内部已实现),因此怎么拼接Slice呢? 对于H264视频流中...
  • h264:设片(slice)的目的是为了限制误码的扩散和传输,应使编码片相互间是独立的。某片的预测不能以其它片中的宏块为参考图像,这样某一片中的预测误差才不会传播到其它片中去 P宏块利用前面已编码图象作为参考...
  • 你的工程中可能包含了很难以直观的理解的硬编码切片,例如s[1:3] 。 对代码的美观、阅读及其维护会造成一定程度的困扰。 下面介绍如何使用切片对象来代替硬编码切片。 首先看一下对比 s = '0123456789' s1 = s[1:...
  • Rust:slice

    千次阅读 2018-12-28 18:22:45
    slice是String的一部分引用。类似切片。 文章目录字符串slice其他slice 字符串slice slice获取值的使用权但是没有得到值得所有权 fn main() { let s = String::from(&amp;amp;quot;Hello world&amp;...
  • slice、aso、fmo、slice groups

    千次阅读 2012-02-22 10:30:10
    以下为在网上找到的一篇...一副图像可以划分为1个或slice,每个silce解码相互独立,且按扫描顺序解码,当然滤波的时候slice边缘块要利用相邻的slice信息。 ASO(Arbitrary Slice Order):如果不使用ASO,那么sl

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 38,365
精华内容 15,346
关键字:

多slice编码