HEVC-HM16.9源碼學習(1)TEncCu::xCompressCU


函數入口:Void TEncSlice::compressSlicem_pcCuEncoder->compressCtu( pCtu );調用xCompressCU( m_ppcBestCU[0], m_ppcTempCU[0], 0 DEBUG_STRING_PASS_INTO(sDebug) );

從CTU開始以四叉樹的結構划分CU,遞歸的每次嘗試各種模式和划分方法,記錄一個最佳的方案並保存參數。

Void TEncCu::xCompressCU( TComDataCU*& rpcBestCU, TComDataCU*& rpcTempCU, const UInt uiDepth DEBUG_STRING_FN_DECLARE(sDebug_), PartSize eParentPartSize )
{
    TComPic* pcPic = rpcBestCU->getPic(); // 獲取當前CU的圖像
    const TComPPS &pps=*(rpcTempCU->getSlice()->getPPS()); // 獲取圖像參數集
    const TComSPS &sps=*(rpcTempCU->getSlice()->getSPS()); // 獲取序列參數集
    
    m_ppcOrigYuv[uiDepth]->copyFromPicYuv( pcPic->getPicYuvOrg(), rpcBestCU->getCtuRsAddr(), rpcBestCU->getZorderIdxInCtu() ); // 從圖像中獲取原始YUV數據
    
    Bool    doNotBlockPu = true; // 快速cbf標識(cbf模式見術語表)
    Bool    earlyDetectionSkipMode = false; //early skip早期跳出標識(early skip模式見術語表)
    
    const UInt uiLPelX   = rpcBestCU->getCUPelX(); // 最左端點x坐標
    const UInt uiRPelX   = uiLPelX + rpcBestCU->getWidth(0)  - 1; // 最右端點x坐標
    const UInt uiTPelY   = rpcBestCU->getCUPelY(); // 最上端點y坐標
    const UInt uiBPelY   = uiTPelY + rpcBestCU->getHeight(0) - 1; // 最下端點y坐標
    const UInt uiWidth   = rpcBestCU->getWidth(0); // 當前CU塊寬度
    
    Int iBaseQP = xComputeQP( rpcBestCU, uiDepth );
    // 傳入當前CU和深度,計算對當前CU的QP;如果不是對每個CU自適應的改變QP,則直接用之前slice算出的QP
    
    const UInt numberValidComponents = rpcBestCU->getPic()->getNumberValidComponents();
    // 獲取成分數量,如果色度格式是CHROMA_400,數量為1,反之為3(最大)
    
    /* 【省略代碼】根據當前深度、是否使用碼率控制、是否使用TQB(TransquantBypass模式,見術語表)調整QP最大和最小的范圍(iMinQP-iMaxQP) */
    
    TComSlice * pcSlice = rpcTempCU->getPic()->getSlice(rpcTempCU->getPic()->getCurrSliceIdx()); // 獲取當前所在slice
    
    const Bool bBoundary = !( uiRPelX < sps.getPicWidthInLumaSamples() && uiBPelY < sps.getPicHeightInLumaSamples() ); // 當前CU塊的右邊界在整個圖像的最右邊 或者 下邊界在整個圖像最下邊 則為TRUE(即在邊界)
    
    if ( !bBoundary ) // 如果不在邊界
    {
        for (Int iQP=iMinQP; iQP<=iMaxQP; iQP++) // 在之前確定的QP范圍中枚舉QP
        {
            /* 【省略代碼】如果是TransquantBypass模式(這里用bIsLosslessMode布爾型標識)且如果當前枚舉到最小QP,將其改為lowestQP */
            /* 【省略代碼】如果是自適應改變QP,設置相關的對最小編碼塊大小取Log的值、色度QP偏移量索引*/
            
            rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
            // 使用CTU四叉樹子層的deltaQP初始化預測數據,根據深度設置CU的寬度和高度,對QP賦值
            
            /* {做幀間預測, SKIP和2Nx2N} */
            if( rpcBestCU->getSlice()->getSliceType() != I_SLICE )
            {
                /* {2Nx2N} */
                if(m_pcEncCfg->getUseEarlySkipDetection()) // 使用early skip早期跳出模式
                {
                    xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2Nx2N DEBUG_STRING_PASS_INTO(sDebug) ); // 嘗試用普通模式進行預測
                    rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode ); // rpcBestCU保存性能最優的預測方式下的參數,rpcTempCU是每次用於嘗試划分預測的CU,每次做完后重新恢復初始化
                }
                /* {SKIP} */
                xCheckRDCostMerge2Nx2N( rpcBestCU, rpcTempCU DEBUG_STRING_PASS_INTO(sDebug), &earlyDetectionSkipMode ); // 嘗試用Merge模式進行預測,傳入早期跳出標識,如果模式為skip則修改該布爾值
                rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
                
                if(!m_pcEncCfg->getUseEarlySkipDetection())
                {
                    /* {2Nx2N, NxN(?講道理,真沒找到這個NxN哪里做了)} */
                    xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2Nx2N DEBUG_STRING_PASS_INTO(sDebug) );
                    rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
                    if(m_pcEncCfg->getUseCbfFastMode()) // 使用快速cbf模式
                    {
                        doNotBlockPu = rpcBestCU->getQtRootCbf( 0 ) != 0; // 判斷四叉樹根節點的CBFlag如果為true,則不需要后續繼續划分
                    }
                }
            }
        }
        
        
        if(!earlyDetectionSkipMode) // 如果之前沒有設置提前跳出,繼續嘗試所有的划分方式
        {
            for (Int iQP=iMinQP; iQP<=iMaxQP; iQP++) // 枚舉QP
            {
                /* 【省略代碼】如果是TransquantBypass模式同上處理 */
                rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode ); // CU恢復初始化
                
                /* {幀間預測, NxN, 2NxN, Nx2N} */
                if( rpcBestCU->getSlice()->getSliceType() != I_SLICE )
                {
                    /* {2Nx2N, NxN} */
                    if(!( (rpcBestCU->getWidth(0)==8) && (rpcBestCU->getHeight(0)==8) )) // 當前CU划分到最小(8*8)
                    {
                        if( uiDepth == sps.getLog2DiffMaxMinCodingBlockSize() && doNotBlockPu) // 如果當前塊的深度為當前的四叉樹底層 且不滿足跳出快速cbf條件
                        {
                            xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_NxN DEBUG_STRING_PASS_INTO(sDebug)   ); // 做NxN的普通預測
                            rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
                        }
                    }
                    /* {Nx2N} */
                    if(doNotBlockPu) // 不滿足跳出快速cbf條件
                    {
                        xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_Nx2N DEBUG_STRING_PASS_INTO(sDebug)  );
                        rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
                        if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_Nx2N ) // 如果使用快速CBF策略 且(剛剛嘗試的)Nx2N是最佳的划分
                        {
                            doNotBlockPu = rpcBestCU->getQtRootCbf( 0 ) != 0; // 判斷四叉樹根節點的CBFlag如果為true,則不需要后續繼續划分
                        }
                    }
                    /* {2NxN} */
                    if(doNotBlockPu) // 和上面一樣,就不寫了
                    {
                        xCheckRDCostInter      ( rpcBestCU, rpcTempCU, SIZE_2NxN DEBUG_STRING_PASS_INTO(sDebug)  );
                        rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
                        if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_2NxN)
                        {
                            doNotBlockPu = rpcBestCU->getQtRootCbf( 0 ) != 0;
                        }
                    }
                    
                    /* {嘗試非對稱分割 (SIZE_2NxnU, SIZE_2NxnD, SIZE_nLx2N, SIZE_nRx2N)} */
                    if(sps.getUseAMP() && uiDepth < sps.getLog2DiffMaxMinCodingBlockSize() ) // 如果允許非對稱分割 且 當前塊的深度不是當前的四叉樹底層
                    {
                        /* 【省略代碼】如果AMP_ENC_SPEEDUP(AMP編碼加速)則根據之前嘗試划分的最好情況省去嘗試一些AMP的划分情況,以此達到加快編碼的目的
                        否則,則朴素的嘗試所有的AMP划分方式(這種情況的代碼省去了,下面只解釋加速情況下的代碼)*/
                        Bool bTestAMP_Hor = false, bTestAMP_Ver = false; // 是否使用AMP橫向划分,縱向划分的標識
                        /* 【省略代碼】根據之前划分的最佳模式是橫切或豎切或四分等判斷使用橫向或者豎向的非對稱划分 */
                        /* 【省略代碼】如果AMP_MRG(AMP Merge) 則增加一對bTestMergeAMP_Hor,bTestMergeAMP_Ver標識橫向和縱向划分;
                         在AMG_MRG下,調用xCheckRDCostInter()時在最后增加一個布爾類型為真的參數,會在predInterSearch()函數中對傳入的殘差清零;
                         其他代碼結構與非AMG_MRG相同,因此以下將AMG_MRG預編譯判斷內的部分都省去了 */
                        
                        /* {做橫向的非對稱運動分割} */
                        if ( bTestAMP_Hor ) // 如果可以進行橫向AMP划分
                        {
                            /* {2NxnU} */
                            if(doNotBlockPu) // 和之前的對稱划分的普通模式一樣,只是傳入參數PartSize改為相應的非對稱划分
                            {
                                xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2NxnU DEBUG_STRING_PASS_INTO(sDebug) );                                rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
                                if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_2NxnU )
                                {
                                    doNotBlockPu = rpcBestCU->getQtRootCbf( 0 ) != 0;
                                }
                            }
                            /* {2NxnD} */
                            if(doNotBlockPu)
                            {
                                xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2NxnD DEBUG_STRING_PASS_INTO(sDebug) );
                                rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
                                if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_2NxnD )
                                {
                                    doNotBlockPu = rpcBestCU->getQtRootCbf( 0 ) != 0;
                                }
                            }
                        }
                        
                        /* {做縱向的非對稱運動分割} */
                        if ( bTestAMP_Ver ) // 如果可以進行橫向AMP划分
                        {
                            /* {nLx2N} */
                            if(doNotBlockPu)
                            {
                                xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_nLx2N DEBUG_STRING_PASS_INTO(sDebug) );
                                rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
                                if(m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_nLx2N )
                                {
                                    doNotBlockPu = rpcBestCU->getQtRootCbf( 0 ) != 0;
                                }
                            }
                            /* {nRx2N} */
                            if(doNotBlockPu)
                            {
                                xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_nRx2N DEBUG_STRING_PASS_INTO(sDebug) );
                                rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
                            }
                        }
                        
                    } /* {結束:AMP} */
                    
                } /* {結束:幀間預測} */
                
                /* {幀內預測} */
                if( (rpcBestCU->getSlice()->getSliceType() == I_SLICE) ||
                    ( (!m_pcEncCfg->getDisableIntraPUsInInterSlices()) &&
                      (
                        ( rpcBestCU->getCbf( 0, COMPONENT_Y  ) != 0 ) ||
                        ( (rpcBestCU->getCbf( 0, COMPONENT_Cb ) != 0) && (numberValidComponents > COMPONENT_Cb) ) ||
                        ( (rpcBestCU->getCbf( 0, COMPONENT_Cr ) != 0) && (numberValidComponents > COMPONENT_Cr) )
                      )
                    )
                  ) // 如果是I幀 或者 允許做幀間預測且CU已被標記CBF(預測殘差為0)
                {
                    xCheckRDCostIntra( rpcBestCU, rpcTempCU, SIZE_2Nx2N DEBUG_STRING_PASS_INTO(sDebug) ); // 嘗試2Nx2N幀內預測
                    rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
                    if( uiDepth == sps.getLog2DiffMaxMinCodingBlockSize() ) // 如果當前深度為四叉樹最底層
                    {
                        if( rpcTempCU->getWidth(0) > ( 1 << sps.getQuadtreeTULog2MinSize() ) ) // 如果當前CU寬度大於最小的TU寬度
                        {
                            xCheckRDCostIntra( rpcBestCU, rpcTempCU, SIZE_NxN DEBUG_STRING_PASS_INTO(sDebug)   ); // 嘗試NxN幀內預測
                            rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
                        }
                    }
                } /* {結束:幀內預測} */
                
                /* {嘗試PCM模式(PCM見術語表)} */
                if(sps.getUsePCM()
                   && rpcTempCU->getWidth(0) <= (1<<sps.getPCMLog2MaxSize())
                   && rpcTempCU->getWidth(0) >= (1<<sps.getPCMLog2MinSize()) ) // 如果允許PCM且當前CU的寬度在PCM最小到最大范圍內
                {
                    UInt uiRawBits = getTotalBits(rpcBestCU->getWidth(0), rpcBestCU->getHeight(0), rpcBestCU->getPic()->getChromaFormat(), sps.getBitDepths().recon); // 直接傳遞整個CU像素的碼率
                    UInt uiBestBits = rpcBestCU->getTotalBits(); // 對CU進行最佳預測編碼的碼率
                    if((uiBestBits > uiRawBits) || (rpcBestCU->getTotalCost() > m_pcRdCost->calcRdCost(uiRawBits, 0)))
                    { // 如果進行預測編碼的碼率大於傳遞整個CU像素的碼率 或者 前者的RDO大於后者的RDO
                        xCheckIntraPCM (rpcBestCU, rpcTempCU); // 嘗試使用PCM模式
                        rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
                    }
                }
                
            } /* {結束:枚舉iQP} */
        }/* {結束:嘗試所有划分方式} */
        
        if( rpcBestCU->getTotalCost() != MAX_DOUBLE ) // 正在測試的配置沒有超過最大字節數,進行熵編碼
        {
            m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[uiDepth][CI_NEXT_BEST]);
            m_pcEntropyCoder->resetBits(); // 重置碼率
            m_pcEntropyCoder->encodeSplitFlag( rpcBestCU, 0, uiDepth, true ); // 對分割標志進行編碼
            rpcBestCU->getTotalBits() += m_pcEntropyCoder->getNumberOfWrittenBits(); // split bits
            rpcBestCU->getTotalBins() += ((TEncBinCABAC *)((TEncSbac*)m_pcEntropyCoder->m_pcEntropyCoderIf)->getEncBinIf())->getBinsCoded(); // 計算熵編碼碼率
            rpcBestCU->getTotalCost()  = m_pcRdCost->calcRdCost( rpcBestCU->getTotalBits(), rpcBestCU->getTotalDistortion() );
            m_pcRDGoOnSbacCoder->store(m_pppcRDSbacCoder[uiDepth][CI_NEXT_BEST]); // 計算總的RD Cost
        }
        
    } /* {結束:如果不在邊界的判斷} */
    
    if( rpcBestCU->getTotalCost()!=MAX_DOUBLE && rpcBestCU->isLosslessCoded(0) && (rpcBestCU->getIPCMFlag(0) == false))
    {
        xFillPCMBuffer(rpcBestCU, m_ppcOrigYuv[uiDepth]); // 將原始YUV樣本復制到PCM緩沖區
    }
    
    /* 【省略代碼】根據最大CUDeltaQP深度、是否使用碼率控制調整QP最大和最小的范圍(iMinQP-iMaxQP) */
    
    const Bool bSubBranch = bBoundary || !( m_pcEncCfg->getUseEarlyCU() && rpcBestCU->getTotalCost()!=MAX_DOUBLE && rpcBestCU->isSkipped(0) ); // 是否繼續划分四叉樹標識(Early CU見術語表)
    
    if( bSubBranch && uiDepth < sps.getLog2DiffMaxMinCodingBlockSize() && (!getFastDeltaQp() || uiWidth > fastDeltaQPCuMaxSize || bBoundary)) // 如果可以繼續划分並且當前深度不在四叉樹最底層
    {
        for (Int iQP=iMinQP; iQP<=iMaxQP; iQP++) // 枚舉QP
        {
            UChar       uhNextDepth         = uiDepth+1; // 下一層的深度
            TComDataCU* pcSubBestPartCU     = m_ppcBestCU[uhNextDepth]; // 下一層的最好CU數組
            TComDataCU* pcSubTempPartCU     = m_ppcTempCU[uhNextDepth]; // 下一層的臨時CU數組
            
            for ( UInt uiPartUnitIdx = 0; uiPartUnitIdx < 4; uiPartUnitIdx++ ) // 枚舉划分四叉樹的四個子塊的下標
            {
                pcSubBestPartCU->initSubCU( rpcTempCU, uiPartUnitIdx, uhNextDepth, iQP ); // 清空或初始化BestCU子塊的數據
                pcSubTempPartCU->initSubCU( rpcTempCU, uiPartUnitIdx, uhNextDepth, iQP ); // 清空或初始化TempCU子塊的數據
                
                if( ( pcSubBestPartCU->getCUPelX() < sps.getPicWidthInLumaSamples() ) && ( pcSubBestPartCU->getCUPelY() < sps.getPicHeightInLumaSamples() ) ) // 子塊CU的橫縱坐標位置在亮度樣本圖像之內(可以繼續往下迭代)
                {
                    if ( 0 == uiPartUnitIdx) // 如果迭代到第一塊子塊(左上角)
                    {
                        m_pppcRDSbacCoder[uhNextDepth][CI_CURR_BEST]->load(m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST]); // 使用之前(當前深度)的緩存初始化RDO
                    }
                    else // 迭代其他子塊
                    {
                        m_pppcRDSbacCoder[uhNextDepth][CI_CURR_BEST]->load(m_pppcRDSbacCoder[uhNextDepth][CI_NEXT_BEST]); // 使用現在(下一深度)的緩存初始RDO
                    }
                    
                    /* 【省略代碼】如果使用AMP_ENC_SPEEDUP(與在AMP加速是同一個),在遞歸調用xCompressXU()時在最后增加一個PartSize參數
                     僅用於在加速的AMP決策時判斷較優的划分方式用;總之不管使不使用AMP加速,這里都要遞歸調用子塊的compressCU*/
                    
                    xCompressCU( pcSubBestPartCU, pcSubTempPartCU, uhNextDepth ); // 遞歸下一層的子塊
                    
                    rpcTempCU->copyPartFrom( pcSubBestPartCU, uiPartUnitIdx, uhNextDepth ); // 將最好的子塊的數據存在當前的臨時數據中
                    xCopyYuv2Tmp( pcSubBestPartCU->getTotalNumPart()*uiPartUnitIdx, uhNextDepth ); // 復制預測圖像和重建圖像的YUV數據
                } /* {結束:可以繼續往下迭代} */
                else
                {
                    pcSubBestPartCU->copyToPic( uhNextDepth ); // 將當前預測的部分復制到圖片中的CU,用於預測下一個子塊
                    rpcTempCU->copyPartFrom( pcSubBestPartCU, uiPartUnitIdx, uhNextDepth ); // 將最好的子塊的數據存在當前的臨時數據中
                }
            }/* {結束:枚舉四叉樹的子塊} */
            
            m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[uhNextDepth][CI_NEXT_BEST]); // 使用現在(下一深度)的緩存初始RDO
            
            if( !bBoundary ) // 如果當前塊不在邊界,進行熵編碼
            {
                m_pcEntropyCoder->resetBits(); // 重置碼率
                m_pcEntropyCoder->encodeSplitFlag( rpcTempCU, 0, uiDepth, true ); // 對分割標志進行編碼
                rpcTempCU->getTotalBits() += m_pcEntropyCoder->getNumberOfWrittenBits(); // split bits
                rpcTempCU->getTotalBins() += ((TEncBinCABAC *)((TEncSbac*)m_pcEntropyCoder->m_pcEntropyCoderIf)->getEncBinIf())->getBinsCoded(); // 計算熵編碼碼率
            }
            rpcTempCU->getTotalCost()  = m_pcRdCost->calcRdCost( rpcTempCU->getTotalBits(), rpcTempCU->getTotalDistortion() ); // 計算總的RD Cost
            
            if( uiDepth == pps.getMaxCuDQPDepth() && pps.getUseDQP()) // 如果使用DeltaQP且當前深度到達DeltaQP最大深度
            {
                Bool hasResidual = false; // 是否有殘差的標識
                for( UInt uiBlkIdx = 0; uiBlkIdx < rpcTempCU->getTotalNumPart(); uiBlkIdx ++) // 枚舉所有划分到最小的CU塊
                {
                    if( (     rpcTempCU->getCbf(uiBlkIdx, COMPONENT_Y)
                         || (rpcTempCU->getCbf(uiBlkIdx, COMPONENT_Cb) && (numberValidComponents > COMPONENT_Cb))
                         || (rpcTempCU->getCbf(uiBlkIdx, COMPONENT_Cr) && (numberValidComponents > COMPONENT_Cr)) ) )
                    { // Cbf != 0 代表有殘差
                        hasResidual = true; // 標識有殘差為true
                        break;
                    }
                }
                
                if ( hasResidual ) // 如果有殘差,進行熵編碼
                {
                    m_pcEntropyCoder->resetBits();
                    m_pcEntropyCoder->encodeQP( rpcTempCU, 0, false );
                    rpcTempCU->getTotalBits() += m_pcEntropyCoder->getNumberOfWrittenBits(); // dQP bits
                    rpcTempCU->getTotalBins() += ((TEncBinCABAC *)((TEncSbac*)m_pcEntropyCoder->m_pcEntropyCoderIf)->getEncBinIf())->getBinsCoded();
                    rpcTempCU->getTotalCost()  = m_pcRdCost->calcRdCost( rpcTempCU->getTotalBits(), rpcTempCU->getTotalDistortion() ); // 之前都寫過了
                    
                    Bool foundNonZeroCbf = false; // 找到非零cbf標識
                    rpcTempCU->setQPSubCUs( rpcTempCU->getRefQP( 0 ), 0, uiDepth, foundNonZeroCbf ); // 設置子塊QP
                    assert( foundNonZeroCbf );
                }
                else // 所有最小CU都沒有殘差
                {
                    rpcTempCU->setQPSubParts( rpcTempCU->getRefQP( 0 ), 0, uiDepth ); // 將子塊QP設置為默認值
                }
            } /* {結束:處理DeltaQP情況} */
            
            m_pcRDGoOnSbacCoder->store(m_pppcRDSbacCoder[uiDepth][CI_TEMP_BEST]); // 存儲當前深度緩存的臨時最優RD Cost
            
            
            if (rpcBestCU->getTotalCost() != MAX_DOUBLE) // 正在測試的配置沒有超過最大字節數
            {
                const Bool isEndOfSlice        =    pcSlice->getSliceMode()==FIXED_NUMBER_OF_BYTES
                && ((pcSlice->getSliceBits()+rpcBestCU->getTotalBits())>pcSlice->getSliceArgument()<<3)
                && rpcBestCU->getCtuRsAddr() != pcPic->getPicSym()->getCtuTsToRsAddrMap(pcSlice->getSliceCurStartCtuTsAddr())
                && rpcBestCU->getCtuRsAddr() != pcPic->getPicSym()->getCtuTsToRsAddrMap(pcSlice->getSliceSegmentCurStartCtuTsAddr()); // 是否是Slice最末的標識
                const Bool isEndOfSliceSegment =    pcSlice->getSliceSegmentMode()==FIXED_NUMBER_OF_BYTES
                && ((pcSlice->getSliceSegmentBits()+rpcBestCU->getTotalBits()) > pcSlice->getSliceSegmentArgument()<<3)
                && rpcBestCU->getCtuRsAddr() != pcPic->getPicSym()->getCtuTsToRsAddrMap(pcSlice->getSliceSegmentCurStartCtuTsAddr()); // 是否是SS最末的標識
                
                if(isEndOfSlice || isEndOfSliceSegment) //由於切片段是切片的子集,因此不需要檢查切片段的切片條件
                {
                    rpcBestCU->getTotalCost() = MAX_DOUBLE; // 如果是最末端,將RD Cost設置為最大字節數
                }
            }
            
            
            xCheckBestMode( rpcBestCU, rpcTempCU, uiDepth DEBUG_STRING_PASS_INTO(sDebug) DEBUG_STRING_PASS_INTO(sTempDebug) DEBUG_STRING_PASS_INTO(false) ); // 對RD Cost進行比較,檢查最好的方式
            
        } /* {結束:枚舉iQP} */
    } /* {結束:可以繼續划分} */
    
    
    /* {子層和遞歸結束返回父層的每個塊都要進行以下的部分} */
    
    rpcBestCU->copyToPic(uiDepth); // 復制最好方式的數據用於下一個塊的預測
    xCopyYuv2Pic( rpcBestCU->getPic(), rpcBestCU->getCtuRsAddr(), rpcBestCU->getZorderIdxInCtu(), uiDepth, uiDepth ); // 復制預測圖像和重建圖像的YUV數據
    
    
}
/****************      Comment By HazelNut      ******************/

 

基於理解加推測寫的解釋,可能有錯誤之處,歡迎留言與討論。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM