Skip to content
Snippets Groups Projects
EncGOP.cpp 150 KiB
Newer Older
  • Learn to ignore specific revisions
  •         refIdx0 = -1;
            refIdx1 = -1;
    
            // search nearest backward POC in List 0
            for ( ref = 0; ref < pcSlice->getNumRefIdx( REF_PIC_LIST_0 ); ref++ )
            {
              int poc = pcSlice->getRefPic( REF_PIC_LIST_0, ref )->getPOC();
    
    Daniel's avatar
    Daniel committed
              if ( poc > currPOC && (poc < backwardPOC || refIdx0 == -1) )
    
    Daniel's avatar
    Daniel committed
                backwardPOC = poc;
    
                refIdx0 = ref;
              }
            }
    
            // search nearest forward POC in List 1
            for ( ref = 0; ref < pcSlice->getNumRefIdx( REF_PIC_LIST_1 ); ref++ )
            {
              int poc = pcSlice->getRefPic( REF_PIC_LIST_1, ref )->getPOC();
              if ( poc < currPOC && (poc > forwardPOC || refIdx1 == -1) )
              {
                forwardPOC = poc;
                refIdx1 = ref;
              }
            }
          }
    
    
    Daniel's avatar
    Daniel committed
          if ( forwardPOC < currPOC && backwardPOC > currPOC )
    
          {
            pcSlice->setBiDirPred( true, refIdx0, refIdx1 );
          }
          else
          {
            pcSlice->setBiDirPred( false, -1, -1 );
          }
        }
        else
        {
          pcSlice->setBiDirPred( false, -1, -1 );
        }
    
    
        double lambda            = 0.0;
        int actualHeadBits       = 0;
        int actualTotalBits      = 0;
        int estimatedBits        = 0;
        int tmpBitsBeforeWriting = 0;
        if ( m_pcCfg->getUseRateCtrl() ) // TODO: does this work with multiple slices and slice-segments?
        {
          int frameLevel = m_pcRateCtrl->getRCSeq()->getGOPID2Level( iGOPid );
    
          if ( pcPic->slices[0]->isIRAP() )
    
          {
            frameLevel = 0;
          }
          m_pcRateCtrl->initRCPic( frameLevel );
          estimatedBits = m_pcRateCtrl->getRCPic()->getTargetBits();
    
    #if U0132_TARGET_BITS_SATURATION
          if (m_pcRateCtrl->getCpbSaturationEnabled() && frameLevel != 0)
          {
            int estimatedCpbFullness = m_pcRateCtrl->getCpbState() + m_pcRateCtrl->getBufferingRate();
    
            // prevent overflow
            if (estimatedCpbFullness - estimatedBits > (int)(m_pcRateCtrl->getCpbSize()*0.9f))
            {
              estimatedBits = estimatedCpbFullness - (int)(m_pcRateCtrl->getCpbSize()*0.9f);
            }
    
            estimatedCpbFullness -= m_pcRateCtrl->getBufferingRate();
            // prevent underflow
    #if V0078_ADAPTIVE_LOWER_BOUND
            if (estimatedCpbFullness - estimatedBits < m_pcRateCtrl->getRCPic()->getLowerBound())
            {
    
              estimatedBits = std::max(200, estimatedCpbFullness - m_pcRateCtrl->getRCPic()->getLowerBound());
    
            }
    #else
            if (estimatedCpbFullness - estimatedBits < (int)(m_pcRateCtrl->getCpbSize()*0.1f))
            {
    
              estimatedBits = std::max(200, estimatedCpbFullness - (int)(m_pcRateCtrl->getCpbSize()*0.1f));
    
            }
    #endif
    
            m_pcRateCtrl->getRCPic()->setTargetBits(estimatedBits);
          }
    #endif
    
          int sliceQP = m_pcCfg->getInitialQP();
          if ( ( pcSlice->getPOC() == 0 && m_pcCfg->getInitialQP() > 0 ) || ( frameLevel == 0 && m_pcCfg->getForceIntraQP() ) ) // QP is specified
          {
            int    NumberBFrames = ( m_pcCfg->getGOPSize() - 1 );
            double dLambda_scale = 1.0 - Clip3( 0.0, 0.5, 0.05*(double)NumberBFrames );
            double dQPFactor     = 0.57*dLambda_scale;
            int    SHIFT_QP      = 12;
            int bitdepth_luma_qp_scale =
              6
              * (pcSlice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA) - 8
                 - DISTORTION_PRECISION_ADJUSTMENT(pcSlice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA)));
            double qp_temp = (double) sliceQP + bitdepth_luma_qp_scale - SHIFT_QP;
            lambda = dQPFactor*pow( 2.0, qp_temp/3.0 );
          }
          else if ( frameLevel == 0 )   // intra case, but use the model
          {
            m_pcSliceEncoder->calCostSliceI(pcPic); // TODO: This only analyses the first slice segment - what about the others?
    
            if ( m_pcCfg->getIntraPeriod() != 1 )   // do not refine allocated bits for all intra case
            {
              int bits = m_pcRateCtrl->getRCSeq()->getLeftAverageBits();
              bits = m_pcRateCtrl->getRCPic()->getRefineBitsForIntra( bits );
    
    #if U0132_TARGET_BITS_SATURATION
              if (m_pcRateCtrl->getCpbSaturationEnabled() )
              {
                int estimatedCpbFullness = m_pcRateCtrl->getCpbState() + m_pcRateCtrl->getBufferingRate();
    
                // prevent overflow
                if (estimatedCpbFullness - bits > (int)(m_pcRateCtrl->getCpbSize()*0.9f))
                {
                  bits = estimatedCpbFullness - (int)(m_pcRateCtrl->getCpbSize()*0.9f);
                }
    
                estimatedCpbFullness -= m_pcRateCtrl->getBufferingRate();
                // prevent underflow
    #if V0078_ADAPTIVE_LOWER_BOUND
                if (estimatedCpbFullness - bits < m_pcRateCtrl->getRCPic()->getLowerBound())
                {
                  bits = estimatedCpbFullness - m_pcRateCtrl->getRCPic()->getLowerBound();
                }
    #else
                if (estimatedCpbFullness - bits < (int)(m_pcRateCtrl->getCpbSize()*0.1f))
                {
                  bits = estimatedCpbFullness - (int)(m_pcRateCtrl->getCpbSize()*0.1f);
                }
    #endif
              }
    #endif
    
              if ( bits < 200 )
              {
                bits = 200;
              }
              m_pcRateCtrl->getRCPic()->setTargetBits( bits );
            }
    
            list<EncRCPic*> listPreviousPicture = m_pcRateCtrl->getPicList();
            m_pcRateCtrl->getRCPic()->getLCUInitTargetBits();
    
            lambda  = m_pcRateCtrl->getRCPic()->estimatePicLambda( listPreviousPicture, pcSlice->isIRAP());
    
            sliceQP = m_pcRateCtrl->getRCPic()->estimatePicQP( lambda, listPreviousPicture );
          }
          else    // normal case
          {
            list<EncRCPic*> listPreviousPicture = m_pcRateCtrl->getPicList();
    
            lambda  = m_pcRateCtrl->getRCPic()->estimatePicLambda( listPreviousPicture, pcSlice->isIRAP());
    
            sliceQP = m_pcRateCtrl->getRCPic()->estimatePicQP( lambda, listPreviousPicture );
          }
    
          sliceQP = Clip3( -pcSlice->getSPS()->getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, sliceQP );
          m_pcRateCtrl->getRCPic()->setPicEstQP( sliceQP );
    
          m_pcSliceEncoder->resetQP( pcPic, sliceQP, lambda );
        }
    
        uint32_t uiNumSliceSegments = 1;
    
        {
          pcSlice->setDefaultClpRng( *pcSlice->getSPS() );
        }
    
        // Allocate some coders, now the number of tiles are known.
        const uint32_t numberOfCtusInFrame = pcPic->cs->pcv->sizeInCtus;
        const int numSubstreamsColumns = (pcSlice->getPPS()->getNumTileColumnsMinus1() + 1);
        const int numSubstreamRows     = pcSlice->getPPS()->getEntropyCodingSyncEnabledFlag() ? pcPic->cs->pcv->heightInCtus : (pcSlice->getPPS()->getNumTileRowsMinus1() + 1);
    
    Karsten Suehring's avatar
    Karsten Suehring committed
    #if JVET_N0857_TILES_BRICKS
        const int numSubstreams        = std::max<int> (numSubstreamRows * numSubstreamsColumns, (int) pcPic->brickMap->bricks.size());
    #else
    
        const int numSubstreams        = numSubstreamRows * numSubstreamsColumns;
    
    Karsten Suehring's avatar
    Karsten Suehring committed
    #endif
    
        std::vector<OutputBitstream> substreamsOut(numSubstreams);
    
    #if ENABLE_QPA
    
        pcPic->m_uEnerHpCtu.resize (numberOfCtusInFrame);
        pcPic->m_iOffsetCtu.resize (numberOfCtusInFrame);
    
    #if ENABLE_QPA_SUB_CTU
        if (pcSlice->getPPS()->getUseDQP() && pcSlice->getPPS()->getCuQpDeltaSubdiv() > 0)
    
    #if MAX_TB_SIZE_SIGNALLING
          const unsigned   mtsLog2 = (unsigned)g_aucLog2[std::min (pcPic->cs->sps->getMaxTbSize(), pcv.maxCUWidth)];
    #else
          const unsigned   mtsLog2 = (unsigned)g_aucLog2[std::min<uint32_t> (MAX_TB_SIZEY, pcv.maxCUWidth)];
    #endif
    
          pcPic->m_subCtuQP.resize ((pcv.maxCUWidth >> mtsLog2) * (pcv.maxCUHeight >> mtsLog2));
        }
    
        if (pcSlice->getSPS()->getSAOEnabledFlag())
    
        {
          pcPic->resizeSAO( numberOfCtusInFrame, 0 );
          pcPic->resizeSAO( numberOfCtusInFrame, 1 );
        }
    
        // it is used for signalling during CTU mode decision, i.e. before ALF processing
    
        if( pcSlice->getSPS()->getALFEnabledFlag() )
    
        {
          pcPic->resizeAlfCtuEnableFlag( numberOfCtusInFrame );
    
    #if JVET_N0415_CTB_ALF
          pcPic->resizeAlfCtbFilterIndex(numberOfCtusInFrame);
    #else
    
          // reset the APS ALF parameters
          AlfSliceParam newALFParam;
          pcSlice->getAPS()->setAlfAPSParam(newALFParam);
    
        }
    
        bool decPic = false;
        bool encPic = false;
        // test if we can skip the picture entirely or decode instead of encoding
        trySkipOrDecodePicture( decPic, encPic, *m_pcCfg, pcPic );
    
        pcPic->cs->slice = pcSlice; // please keep this
    
    #if ENABLE_QPA
        if (pcSlice->getPPS()->getSliceChromaQpFlag() && CS::isDualITree (*pcSlice->getPic()->cs) && !m_pcCfg->getUsePerceptQPA() && (m_pcCfg->getSliceChromaOffsetQpPeriodicity() == 0))
    #else
        if (pcSlice->getPPS()->getSliceChromaQpFlag() && CS::isDualITree (*pcSlice->getPic()->cs))
    #endif
    
        {
          // overwrite chroma qp offset for dual tree
          pcSlice->setSliceChromaQpDelta(COMPONENT_Cb, m_pcCfg->getChromaCbQpOffsetDualTree());
          pcSlice->setSliceChromaQpDelta(COMPONENT_Cr, m_pcCfg->getChromaCrQpOffsetDualTree());
    
    #if JVET_N0054_JOINT_CHROMA
          pcSlice->setSliceChromaQpDelta(JOINT_CbCr,   m_pcCfg->getChromaCbCrQpOffsetDualTree());
    #endif
    
          m_pcSliceEncoder->setUpLambda(pcSlice, pcSlice->getLambdas()[0], pcSlice->getSliceQp());
        }
    
        if (pcSlice->getSPS()->getUseReshaper())
        {
          m_pcReshaper->getReshapeCW()->rspTid = pcSlice->getTLayer() + (pcSlice->isIntra() ? 0 : 1);
          m_pcReshaper->getReshapeCW()->rspSliceQP = pcSlice->getSliceQp();
    
          m_pcReshaper->setSrcReshaped(false);
          m_pcReshaper->setRecReshaped(true);
    
          if (m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_PQ)
          {
    
            m_pcReshaper->preAnalyzerHDR(pcPic, pcSlice->getSliceType(), m_pcCfg->getReshapeCW(), m_pcCfg->getDualITree());
    
          }
          else if (m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_SDR)
          {
    
            m_pcReshaper->preAnalyzerSDR(pcPic, pcSlice->getSliceType(), m_pcCfg->getReshapeCW(), m_pcCfg->getDualITree());
    
            THROW("Reshaper for other signal currently not defined!");
    
    Taoran Lu's avatar
    Taoran Lu committed
            if (m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_PQ)
            {
    
              m_pcReshaper->initLUTfromdQPModel();
              m_pcEncLib->getRdCost()->updateReshapeLumaLevelToWeightTableChromaMD(m_pcReshaper->getInvLUT());
    
    Taoran Lu's avatar
    Taoran Lu committed
            }
            else if (m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_SDR)
            {
    
              if (m_pcReshaper->getReshapeFlag())
              {
                m_pcReshaper->constructReshaperSDR();
                m_pcEncLib->getRdCost()->updateReshapeLumaLevelToWeightTable(m_pcReshaper->getSliceReshaperInfo(), m_pcReshaper->getWeightTable(), m_pcReshaper->getCWeight());
              }
    
              THROW("Reshaper for other signal currently not defined!");
    
            m_pcReshaper->setCTUFlag(false);
    
            //reshape original signal
            if (m_pcReshaper->getSliceReshaperInfo().getUseSliceReshaper())
            {
              pcPic->getOrigBuf(COMPONENT_Y).rspSignal(m_pcReshaper->getFwdLUT());
              m_pcReshaper->setSrcReshaped(true);
              m_pcReshaper->setRecReshaped(true);
            }
          }
          else
          {
            if (!m_pcReshaper->getReshapeFlag())
            {
    
    Taoran Lu's avatar
    Taoran Lu committed
              m_pcReshaper->setCTUFlag(false);
            }
            else
    
              m_pcReshaper->setCTUFlag(true);
    
            m_pcReshaper->getSliceReshaperInfo().setSliceReshapeModelPresentFlag(false);
    
            if (m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_PQ)
            {
              m_pcEncLib->getRdCost()->restoreReshapeLumaLevelToWeightTable();
            }
            else if (m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_SDR)
            {
              int modIP = pcPic->getPOC() - pcPic->getPOC() / m_pcCfg->getReshapeCW().rspFpsToIp * m_pcCfg->getReshapeCW().rspFpsToIp;
              if (m_pcReshaper->getReshapeFlag() && m_pcCfg->getReshapeCW().rspIntraPeriod == -1 && modIP == 0)           // for LDB, update reshaping curve every second
    
                m_pcReshaper->getSliceReshaperInfo().setSliceReshapeModelPresentFlag(true);
                m_pcReshaper->constructReshaperSDR();
                m_pcEncLib->getRdCost()->updateReshapeLumaLevelToWeightTable(m_pcReshaper->getSliceReshaperInfo(), m_pcReshaper->getWeightTable(), m_pcReshaper->getCWeight());
    
            else
            {
              THROW("Reshaper for other signal currently not defined!");
            }
    
    
          m_pcReshaper->copySliceReshaperInfo(pcSlice->getReshapeInfo(), m_pcReshaper->getSliceReshaperInfo());
        }
        else
        {
          m_pcReshaper->setCTUFlag(false);
        }
    
        if( encPic )
        // now compress (trial encode) the various slice segments (slices, and dependent slices)
        {
          DTRACE_UPDATE( g_trace_ctx, ( std::make_pair( "poc", pocCurr ) ) );
    
    
          pcSlice->setSliceCurStartCtuTsAddr( 0 );
    
          for(uint32_t nextCtuTsAddr = 0; nextCtuTsAddr < numberOfCtusInFrame; )
          {
            m_pcSliceEncoder->precompressSlice( pcPic );
            m_pcSliceEncoder->compressSlice   ( pcPic, false, false );
    
            const uint32_t curSliceEnd = pcSlice->getSliceCurEndCtuTsAddr();
            if(curSliceEnd < numberOfCtusInFrame)
            {
              uint32_t independentSliceIdx = pcSlice->getIndependentSliceIdx();
              pcPic->allocateNewSlice();
              m_pcSliceEncoder->setSliceSegmentIdx      (uiNumSliceSegments);
              // prepare for next slice
              pcSlice = pcPic->slices[uiNumSliceSegments];
              CHECK(!(pcSlice->getPPS() != 0), "Unspecified error");
              pcSlice->copySliceInfo(pcPic->slices[uiNumSliceSegments - 1]);
              pcSlice->setSliceCurStartCtuTsAddr(curSliceEnd);
              pcSlice->setSliceBits(0);
              independentSliceIdx++;
              pcSlice->setIndependentSliceIdx(independentSliceIdx);
              uiNumSliceSegments++;
            }
            nextCtuTsAddr = curSliceEnd;
          }
    
          duData.clear();
    
          CodingStructure& cs = *pcPic->cs;
          pcSlice = pcPic->slices[0];
    
    
    Taoran Lu's avatar
    Taoran Lu committed
          if (pcSlice->getSPS()->getUseReshaper() && m_pcReshaper->getSliceReshaperInfo().getUseSliceReshaper())
          {
              CHECK((m_pcReshaper->getRecReshaped() == false), "Rec picture is not reshaped!");
              pcPic->getRecoBuf(COMPONENT_Y).rspSignal(m_pcReshaper->getInvLUT());
              m_pcReshaper->setRecReshaped(false);
    
              pcPic->getOrigBuf().copyFrom(pcPic->getTrueOrigBuf());
          }
    
    
          // SAO parameter estimation using non-deblocked pixels for CTU bottom and right boundary areas
    
          if( pcSlice->getSPS()->getSAOEnabledFlag() && m_pcCfg->getSaoCtuBoundary() )
    
          {
            m_pcSAO->getPreDBFStatistics( cs );
          }
    
          //-- Loop filter
          if ( m_pcCfg->getDeblockingFilterMetric() )
          {
      #if W0038_DB_OPT
            if ( m_pcCfg->getDeblockingFilterMetric()==2 )
            {
              applyDeblockingFilterParameterSelection(pcPic, uiNumSliceSegments, iGOPid);
            }
            else
            {
      #endif
              applyDeblockingFilterMetric(pcPic, uiNumSliceSegments);
      #if W0038_DB_OPT
            }
      #endif
          }
    
          m_pcLoopFilter->loopFilterPic( cs );
    
    
          CS::setRefinedMotionField(cs);
    
          DTRACE_UPDATE( g_trace_ctx, ( std::make_pair( "final", 1 ) ) );
    
    
          if( pcSlice->getSPS()->getSAOEnabledFlag() )
    
          {
            bool sliceEnabled[MAX_NUM_COMPONENT];
            m_pcSAO->initCABACEstimator( m_pcEncLib->getCABACEncoder(), m_pcEncLib->getCtxCache(), pcSlice );
    
    
            m_pcSAO->SAOProcess( cs, sliceEnabled, pcSlice->getLambdas(),
    #if ENABLE_QPA
                                 (m_pcCfg->getUsePerceptQPA() && !m_pcCfg->getUseRateCtrl() && pcSlice->getPPS()->getUseDQP() ? m_pcEncLib->getRdCost (PARL_PARAM0 (0))->getChromaWeight() : 0.0),
    #endif
    
                                 m_pcCfg->getTestSAODisableAtPictureLevel(), m_pcCfg->getSaoEncodingRate(), m_pcCfg->getSaoEncodingRateChroma(), m_pcCfg->getSaoCtuBoundary(), m_pcCfg->getSaoGreedyMergeEnc() );
    
                                 m_pcCfg->getTestSAODisableAtPictureLevel(), m_pcCfg->getSaoEncodingRate(), m_pcCfg->getSaoEncodingRateChroma(), m_pcCfg->getSaoCtuBoundary() );
    
    #endif
            //assign SAO slice header
            for(int s=0; s< uiNumSliceSegments; s++)
            {
              pcPic->slices[s]->setSaoEnabledFlag(CHANNEL_TYPE_LUMA, sliceEnabled[COMPONENT_Y]);
              CHECK(!(sliceEnabled[COMPONENT_Cb] == sliceEnabled[COMPONENT_Cr]), "Unspecified error");
              pcPic->slices[s]->setSaoEnabledFlag(CHANNEL_TYPE_CHROMA, sliceEnabled[COMPONENT_Cb]);
            }
          }
    
    
          if( pcSlice->getSPS()->getALFEnabledFlag() )
    
    #if JVET_N0415_CTB_ALF
            for (int s = 0; s < uiNumSliceSegments; s++)
            {
              pcPic->slices[s]->setTileGroupAlfEnabledFlag(COMPONENT_Y, false);
            }
            m_pcALF->initCABACEstimator(m_pcEncLib->getCABACEncoder(), m_pcEncLib->getCtxCache(), pcSlice, m_pcEncLib->getApsMap());
            m_pcALF->ALFProcess(cs, pcSlice->getLambdas()
    #if ENABLE_QPA
              , (m_pcCfg->getUsePerceptQPA() && !m_pcCfg->getUseRateCtrl() && pcSlice->getPPS()->getUseDQP() ? m_pcEncLib->getRdCost(PARL_PARAM0(0))->getChromaWeight() : 0.0)
    #endif
            );
    
            //assign ALF slice header
            for (int s = 0; s < uiNumSliceSegments; s++)
            {
              pcPic->slices[s]->setTileGroupAlfEnabledFlag(COMPONENT_Y, cs.slice->getTileGroupAlfEnabledFlag(COMPONENT_Y));
              pcPic->slices[s]->setTileGroupAlfEnabledFlag(COMPONENT_Cb, cs.slice->getTileGroupAlfEnabledFlag(COMPONENT_Cb));
              pcPic->slices[s]->setTileGroupAlfEnabledFlag(COMPONENT_Cr, cs.slice->getTileGroupAlfEnabledFlag(COMPONENT_Cr));
              if (pcPic->slices[s]->getTileGroupAlfEnabledFlag(COMPONENT_Y))
              {
                pcPic->slices[s]->setTileGroupNumAps(cs.slice->getTileGroupNumAps());
                pcPic->slices[s]->setAPSs(cs.slice->getTileGroupApsIdLuma());
              }
              else
              {
                pcPic->slices[s]->setTileGroupNumAps(0);
              }
              pcPic->slices[s]->setAPSs(cs.slice->getAPSs());
    
              pcPic->slices[s]->setTileGroupApsIdChroma(cs.slice->getTileGroupApsIdChroma());
    
            }
    #else
            AlfSliceParam alfSliceParam;
            m_pcALF->initCABACEstimator(m_pcEncLib->getCABACEncoder(), m_pcEncLib->getCtxCache(), pcSlice);
    
            m_pcALF->ALFProcess( cs, pcSlice->getLambdas(),
    #if ENABLE_QPA
                                 (m_pcCfg->getUsePerceptQPA() && !m_pcCfg->getUseRateCtrl() && pcSlice->getPPS()->getUseDQP() ? m_pcEncLib->getRdCost (PARL_PARAM0 (0))->getChromaWeight() : 0.0),
    #endif
                                 alfSliceParam );
    
            pcPic->cs->aps->setAlfAPSParam(alfSliceParam);
    
          if (m_pcCfg->getUseCompositeRef() && getPrepareLTRef())
    
          {
            updateCompositeReference(pcSlice, rcListPic, pocCurr);
          }
    
        }
        else // skip enc picture
        {
          pcSlice->setSliceQpBase( pcSlice->getSliceQp() );
    
    
    #if ENABLE_QPA
          if (m_pcCfg->getUsePerceptQPA() && !m_pcCfg->getUseRateCtrl() && pcSlice->getPPS()->getUseDQP())
          {
            const double picLambda = pcSlice->getLambdas()[0];
    
            for (uint32_t ctuRsAddr = 0; ctuRsAddr < numberOfCtusInFrame; ctuRsAddr++)
            {
              pcPic->m_uEnerHpCtu[ctuRsAddr] = picLambda;  // initialize to slice lambda (just for safety)
            }
          }
    #endif
    
          if( pcSlice->getSPS()->getSAOEnabledFlag() )
    
          {
            m_pcSAO->disabledRate( *pcPic->cs, pcPic->getSAO(1), m_pcCfg->getSaoEncodingRate(), m_pcCfg->getSaoEncodingRateChroma());
          }
        }
    
        if( m_pcCfg->getUseAMaxBT() )
        {
          for( const CodingUnit *cu : pcPic->cs->cus )
          {
    
            if( !pcSlice->isIRAP() )
    
            {
              m_uiBlkSize[pcSlice->getDepth()] += cu->Y().area();
              m_uiNumBlk [pcSlice->getDepth()]++;
            }
          }
        }
    
        if( encPic || decPic )
        {
          pcSlice = pcPic->slices[0];
    
          /////////////////////////////////////////////////////////////////////////////////////////////////// File writing
    
          // write various parameter sets
    
    #if JCTVC_Y0038_PARAMS
          bool writePS = m_bSeqFirst || (m_pcCfg->getReWriteParamSets() && (pcSlice->isIRAP()));
          if (writePS)
          {
            m_pcEncLib->setParamSetChanged(pcSlice->getSPS()->getSPSId(), pcSlice->getPPS()->getPPSId());
          }
          actualTotalBits += xWriteParameterSets(accessUnit, pcSlice, writePS);
    
          actualTotalBits += xWriteParameterSets( accessUnit, pcSlice, m_bSeqFirst );
    
          if ( m_bSeqFirst )
    
          {
            // create prefix SEI messages at the beginning of the sequence
            CHECK(!(leadingSeiMessages.empty()), "Unspecified error");
            xCreateIRAPLeadingSEIMessages(leadingSeiMessages, pcSlice->getSPS(), pcSlice->getPPS());
    
            m_bSeqFirst = false;
          }
          if (m_pcCfg->getAccessUnitDelimiter())
          {
            xWriteAccessUnitDelimiter(accessUnit, pcSlice);
          }
    
    #if JVET_N0415_CTB_ALF
          if (pcSlice->getSPS()->getALFEnabledFlag() && pcSlice->getTileGroupAlfEnabledFlag(COMPONENT_Y))
          {
            for (int apsId = 0; apsId < MAX_NUM_APS; apsId++)
            {
              ParameterSetMap<APS> *apsMap = m_pcEncLib->getApsMap();
              APS* aps = apsMap->getPS(apsId);
    
              bool writeAPS = aps && apsMap->getChangedFlag(apsId);
              if( !aps && pcSlice->getAPSs() && pcSlice->getAPSs()[apsId] )
              {
                writeAPS = true;
                aps = pcSlice->getAPSs()[apsId]; // use asp from slice header
                *apsMap->allocatePS( apsId ) = *aps; //allocate and cpy
              }
              if (writeAPS )
    
              {
                actualTotalBits += xWriteAPS(accessUnit, aps);
                apsMap->clearChangedFlag(apsId);
                CHECK(aps != pcSlice->getAPSs()[apsId], "Wrong APS pointer in compressGOP");
              }
            }
          }
    #else
    
          if (pcSlice->getSPS()->getALFEnabledFlag() && pcSlice->getAPS()->getAlfAPSParam().enabledFlag[COMPONENT_Y])
    
    Hendry's avatar
    Hendry committed
          {
    
            pcSlice->setTileGroupAlfEnabledFlag(true);
    
    Hendry's avatar
    Hendry committed
            pcSlice->setAPSId(pcSlice->getAPS()->getAPSId());
            actualTotalBits += xWriteAPS(accessUnit, pcSlice->getAPS());
          }
    
          else
          {
            pcSlice->setTileGroupAlfEnabledFlag(false);
          }
    
    
          // reset presence of BP SEI indication
          m_bufferingPeriodSEIPresentInAU = false;
          // create prefix SEI associated with a picture
          xCreatePerPictureSEIMessages(iGOPid, leadingSeiMessages, nestedSeiMessages, pcSlice);
    
          // pcSlice is currently slice 0.
          std::size_t binCountsInNalUnits   = 0; // For implementation of cabac_zero_word stuffing (section 7.4.3.10)
          std::size_t numBytesInVclNalUnits = 0; // For implementation of cabac_zero_word stuffing (section 7.4.3.10)
    
          for(uint32_t sliceSegmentStartCtuTsAddr = 0, sliceSegmentIdxCount = 0; sliceSegmentStartCtuTsAddr < numberOfCtusInFrame; sliceSegmentIdxCount++, sliceSegmentStartCtuTsAddr = pcSlice->getSliceCurEndCtuTsAddr())
          {
            pcSlice = pcPic->slices[sliceSegmentIdxCount];
            if(sliceSegmentIdxCount > 0 && pcSlice->getSliceType()!= I_SLICE)
            {
              pcSlice->checkColRefIdx(sliceSegmentIdxCount, pcPic);
            }
            m_pcSliceEncoder->setSliceSegmentIdx(sliceSegmentIdxCount);
    
            pcSlice->setRPS   (pcPic->slices[0]->getRPS());
            pcSlice->setRPSidx(pcPic->slices[0]->getRPSidx());
    
            for ( uint32_t ui = 0 ; ui < numSubstreams; ui++ )
            {
              substreamsOut[ui].clear();
            }
    
            /* start slice NALunit */
            OutputNALUnit nalu( pcSlice->getNalUnitType(), pcSlice->getTLayer() );
            m_HLSWriter->setBitstream( &nalu.m_Bitstream );
    
            pcSlice->setNoRaslOutputFlag(false);
            if (pcSlice->isIRAP())
            {
    
    #if !JVET_M0101_HLS
    
              if (pcSlice->getNalUnitType() >= NAL_UNIT_CODED_SLICE_BLA_W_LP && pcSlice->getNalUnitType() <= NAL_UNIT_CODED_SLICE_IDR_N_LP)
    
    #else
              if (pcSlice->getNalUnitType() >= NAL_UNIT_CODED_SLICE_IDR_W_RADL && pcSlice->getNalUnitType() <= NAL_UNIT_CODED_SLICE_IDR_N_LP)
    #endif
    
              {
                pcSlice->setNoRaslOutputFlag(true);
              }
              //the inference for NoOutputPriorPicsFlag
              // KJS: This cannot happen at the encoder
              if (!m_bFirst && pcSlice->isIRAP() && pcSlice->getNoRaslOutputFlag())
              {
                if (pcSlice->getNalUnitType() == NAL_UNIT_CODED_SLICE_CRA)
                {
                  pcSlice->setNoOutputPriorPicsFlag(true);
                }
              }
            }
    
            tmpBitsBeforeWriting = m_HLSWriter->getNumberOfWrittenBits();
            m_HLSWriter->codeSliceHeader( pcSlice );
            actualHeadBits += ( m_HLSWriter->getNumberOfWrittenBits() - tmpBitsBeforeWriting );
    
            pcSlice->setFinalized(true);
    
            pcSlice->clearSubstreamSizes(  );
            {
              uint32_t numBinsCoded = 0;
              m_pcSliceEncoder->encodeSlice(pcPic, &(substreamsOut[0]), numBinsCoded);
              binCountsInNalUnits+=numBinsCoded;
            }
            {
              // Construct the final bitstream by concatenating substreams.
              // The final bitstream is either nalu.m_Bitstream or pcBitstreamRedirect;
              // Complete the slice header info.
              m_HLSWriter->setBitstream( &nalu.m_Bitstream );
              m_HLSWriter->codeTilesWPPEntryPoint( pcSlice );
    
              // Append substreams...
              OutputBitstream *pcOut = pcBitstreamRedirect;
    
    Karsten Suehring's avatar
    Karsten Suehring committed
    #if JVET_N0857_TILES_BRICKS
              const int numZeroSubstreamsAtStartOfSlice  = pcPic->brickMap->getSubstreamForCtuAddr(pcSlice->getSliceCurStartCtuTsAddr(), false, pcSlice);
    
    #else
              const int numZeroSubstreamsAtStartOfSlice  = pcPic->tileMap->getSubstreamForCtuAddr(pcSlice->getSliceCurStartCtuTsAddr(), false, pcSlice);
    #endif
              const int numSubstreamsToCode  = pcSlice->getNumberOfSubstreamSizes()+1;
              for ( uint32_t ui = 0 ; ui < numSubstreamsToCode; ui++ )
              {
                pcOut->addSubstream(&(substreamsOut[ui+numZeroSubstreamsAtStartOfSlice]));
              }
            }
    
            // If current NALU is the first NALU of slice (containing slice header) and more NALUs exist (due to multiple dependent slices) then buffer it.
            // If current NALU is the last NALU of slice and a NALU was buffered, then (a) Write current NALU (b) Update an write buffered NALU at approproate location in NALU list.
            bool bNALUAlignedWrittenToList    = false; // used to ensure current NALU is not written more than once to the NALU list.
            xAttachSliceDataToNalUnit(nalu, pcBitstreamRedirect);
            accessUnit.push_back(new NALUnitEBSP(nalu));
            actualTotalBits += uint32_t(accessUnit.back()->m_nalUnitData.str().size()) * 8;
            numBytesInVclNalUnits += (std::size_t)(accessUnit.back()->m_nalUnitData.str().size());
            bNALUAlignedWrittenToList = true;
    
            if (!bNALUAlignedWrittenToList)
            {
              nalu.m_Bitstream.writeAlignZero();
              accessUnit.push_back(new NALUnitEBSP(nalu));
            }
    
            if( ( m_pcCfg->getPictureTimingSEIEnabled() || m_pcCfg->getDecodingUnitInfoSEIEnabled() ) &&
                ( pcSlice->getSPS()->getVuiParametersPresentFlag() ) &&
                ( ( pcSlice->getSPS()->getVuiParameters()->getHrdParameters()->getNalHrdParametersPresentFlag() )
               || ( pcSlice->getSPS()->getVuiParameters()->getHrdParameters()->getVclHrdParametersPresentFlag() ) ) &&
                ( pcSlice->getSPS()->getVuiParameters()->getHrdParameters()->getSubPicCpbParamsPresentFlag() ) )
            {
                uint32_t numNalus = 0;
              uint32_t numRBSPBytes = 0;
              for (AccessUnit::const_iterator it = accessUnit.begin(); it != accessUnit.end(); it++)
              {
                numRBSPBytes += uint32_t((*it)->m_nalUnitData.str().size());
                numNalus ++;
              }
              duData.push_back(DUData());
              duData.back().accumBitsDU = ( numRBSPBytes << 3 );
              duData.back().accumNalsDU = numNalus;
            }
          } // end iteration over slices
    
    
          // cabac_zero_words processing
          cabac_zero_word_padding(pcSlice, pcPic, binCountsInNalUnits, numBytesInVclNalUnits, accessUnit.back()->m_nalUnitData, m_pcCfg->getCabacZeroWordPaddingEnabled());
    
          //-- For time output for each slice
          auto elapsed = std::chrono::steady_clock::now() - beforeTime;
          auto encTime = std::chrono::duration_cast<std::chrono::seconds>( elapsed ).count();
    
          std::string digestStr;
          if (m_pcCfg->getDecodedPictureHashSEIType()!=HASHTYPE_NONE)
          {
            SEIDecodedPictureHash *decodedPictureHashSei = new SEIDecodedPictureHash();
            PelUnitBuf recoBuf = pcPic->cs->getRecoBuf();
            m_seiEncoder.initDecodedPictureHashSEI(decodedPictureHashSei, recoBuf, digestStr, pcSlice->getSPS()->getBitDepths());
            trailingSeiMessages.push_back(decodedPictureHashSei);
          }
    
          m_pcCfg->setEncodedFlag(iGOPid, true);
    
          double PSNR_Y;
    
          xCalculateAddPSNRs(isField, isTff, iGOPid, pcPic, accessUnit, rcListPic, encTime, snr_conversion, printFrameMSE, &PSNR_Y
                           , isEncodeLtRef
          );
    
    
          // Only produce the Green Metadata SEI message with the last picture.
          if( m_pcCfg->getSEIGreenMetadataInfoSEIEnable() && pcSlice->getPOC() == ( m_pcCfg->getFramesToBeEncoded() - 1 )  )
          {
            SEIGreenMetadataInfo *seiGreenMetadataInfo = new SEIGreenMetadataInfo;
            m_seiEncoder.initSEIGreenMetadataInfo(seiGreenMetadataInfo, (uint32_t)(PSNR_Y * 100 + 0.5));
            trailingSeiMessages.push_back(seiGreenMetadataInfo);
          }
    
          xWriteTrailingSEIMessages(trailingSeiMessages, accessUnit, pcSlice->getTLayer(), pcSlice->getSPS());
    
          printHash(m_pcCfg->getDecodedPictureHashSEIType(), digestStr);
    
          if ( m_pcCfg->getUseRateCtrl() )
          {
            double avgQP     = m_pcRateCtrl->getRCPic()->calAverageQP();
            double avgLambda = m_pcRateCtrl->getRCPic()->calAverageLambda();
            if ( avgLambda < 0.0 )
            {
              avgLambda = lambda;
            }
    
    
            m_pcRateCtrl->getRCPic()->updateAfterPicture( actualHeadBits, actualTotalBits, avgQP, avgLambda, pcSlice->isIRAP());
    
            m_pcRateCtrl->getRCPic()->addToPictureLsit( m_pcRateCtrl->getPicList() );
    
            m_pcRateCtrl->getRCSeq()->updateAfterPic( actualTotalBits );
    
            if ( !pcSlice->isIRAP() )
    
            {
              m_pcRateCtrl->getRCGOP()->updateAfterPicture( actualTotalBits );
            }
            else    // for intra picture, the estimated bits are used to update the current status in the GOP
            {
              m_pcRateCtrl->getRCGOP()->updateAfterPicture( estimatedBits );
            }
      #if U0132_TARGET_BITS_SATURATION
            if (m_pcRateCtrl->getCpbSaturationEnabled())
            {
              m_pcRateCtrl->updateCpbState(actualTotalBits);
              msg( NOTICE, " [CPB %6d bits]", m_pcRateCtrl->getCpbState() );
            }
      #endif
          }
    
          xCreatePictureTimingSEI( m_pcCfg->getEfficientFieldIRAPEnabled() ? effFieldIRAPMap.GetIRAPGOPid() : 0, leadingSeiMessages, nestedSeiMessages, duInfoSeiMessages, pcSlice, isField, duData );
          if( m_pcCfg->getScalableNestingSEIEnabled() )
          {
            xCreateScalableNestingSEI( leadingSeiMessages, nestedSeiMessages );
          }
          xWriteLeadingSEIMessages( leadingSeiMessages, duInfoSeiMessages, accessUnit, pcSlice->getTLayer(), pcSlice->getSPS(), duData );
          xWriteDuSEIMessages( duInfoSeiMessages, accessUnit, pcSlice->getTLayer(), pcSlice->getSPS(), duData );
    
          m_AUWriterIf->outputAU( accessUnit );
    
          msg( NOTICE, "\n" );
          fflush( stdout );
        }
    
    
        DTRACE_UPDATE( g_trace_ctx, ( std::make_pair( "final", 0 ) ) );
    
        pcPic->reconstructed = true;
        m_bFirst = false;
        m_iNumPicCoded++;
    
        if (!(m_pcCfg->getUseCompositeRef() && isEncodeLtRef))
    
        /* logging: insert a newline at end of picture period */
    
        if (m_pcCfg->getEfficientFieldIRAPEnabled())
        {
          iGOPid=effFieldIRAPMap.restoreGOPid(iGOPid);
        }
    
        pcPic->destroyTempBuffers();
        pcPic->cs->destroyCoeffs();
        pcPic->cs->releaseIntermediateData();
      } // iGOPid-loop
    
      delete pcBitstreamRedirect;
    
      CHECK(!( (m_iNumPicCoded == iNumPicRcvd) ), "Unspecified error");
    
    }
    
    
    void EncGOP::printOutSummary(uint32_t uiNumAllPicCoded, bool isField, const bool printMSEBasedSNR, const bool printSequenceMSE, const bool printHexPsnr, const BitDepths &bitDepths)
    
    {
    #if ENABLE_QPA
      const bool    useWPSNR = m_pcEncLib->getUseWPSNR();
    #endif
    #if WCG_WPSNR
    
    Taoran Lu's avatar
    Taoran Lu committed
      const bool    useLumaWPSNR = m_pcEncLib->getLumaLevelToDeltaQPMapping().isEnabled() || (m_pcCfg->getReshaper() && m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_PQ);
    
    #endif
    
      if( m_pcCfg->getDecodeBitstream(0).empty() && m_pcCfg->getDecodeBitstream(1).empty() && !m_pcCfg->useFastForwardToPOC() )
      {
        CHECK( !( uiNumAllPicCoded == m_gcAnalyzeAll.getNumPic() ), "Unspecified error" );
      }
    
      //--CFG_KDY
      const int rateMultiplier=(isField?2:1);
      m_gcAnalyzeAll.setFrmRate( m_pcCfg->getFrameRate()*rateMultiplier / (double)m_pcCfg->getTemporalSubsampleRatio());
      m_gcAnalyzeI.setFrmRate( m_pcCfg->getFrameRate()*rateMultiplier / (double)m_pcCfg->getTemporalSubsampleRatio());
      m_gcAnalyzeP.setFrmRate( m_pcCfg->getFrameRate()*rateMultiplier / (double)m_pcCfg->getTemporalSubsampleRatio());
      m_gcAnalyzeB.setFrmRate( m_pcCfg->getFrameRate()*rateMultiplier / (double)m_pcCfg->getTemporalSubsampleRatio());
    #if WCG_WPSNR
      if (useLumaWPSNR)
      {
        m_gcAnalyzeWPSNR.setFrmRate(m_pcCfg->getFrameRate()*rateMultiplier / (double)m_pcCfg->getTemporalSubsampleRatio());
      }
    #endif
    
      const ChromaFormat chFmt = m_pcCfg->getChromaFormatIdc();
    
      //-- all
      msg( INFO, "\n" );
      msg( DETAILS,"\nSUMMARY --------------------------------------------------------\n" );
    #if ENABLE_QPA
    
      m_gcAnalyzeAll.printOut('a', chFmt, printMSEBasedSNR, printSequenceMSE, printHexPsnr, bitDepths, useWPSNR);
    
      m_gcAnalyzeAll.printOut('a', chFmt, printMSEBasedSNR, printSequenceMSE, printHexPsnr, bitDepths);
    
    #endif
      msg( DETAILS,"\n\nI Slices--------------------------------------------------------\n" );
    
      m_gcAnalyzeI.printOut('i', chFmt, printMSEBasedSNR, printSequenceMSE, printHexPsnr, bitDepths);
    
    
      msg( DETAILS,"\n\nP Slices--------------------------------------------------------\n" );
    
      m_gcAnalyzeP.printOut('p', chFmt, printMSEBasedSNR, printSequenceMSE, printHexPsnr, bitDepths);
    
    
      msg( DETAILS,"\n\nB Slices--------------------------------------------------------\n" );
    
      m_gcAnalyzeB.printOut('b', chFmt, printMSEBasedSNR, printSequenceMSE, printHexPsnr, bitDepths);
    
    #if WCG_WPSNR
      if (useLumaWPSNR)
      {
        msg(DETAILS, "\nWPSNR SUMMARY --------------------------------------------------------\n");
    
        m_gcAnalyzeWPSNR.printOut('w', chFmt, printMSEBasedSNR, printSequenceMSE, printHexPsnr, bitDepths, useLumaWPSNR);
    
        m_gcAnalyzeAll.printSummary(chFmt, printSequenceMSE, printHexPsnr, bitDepths, m_pcCfg->getSummaryOutFilename());
    
        m_gcAnalyzeI.printSummary(chFmt, printSequenceMSE, printHexPsnr, bitDepths, m_pcCfg->getSummaryPicFilenameBase()+"I.txt");
        m_gcAnalyzeP.printSummary(chFmt, printSequenceMSE, printHexPsnr, bitDepths, m_pcCfg->getSummaryPicFilenameBase()+"P.txt");
        m_gcAnalyzeB.printSummary(chFmt, printSequenceMSE, printHexPsnr, bitDepths, m_pcCfg->getSummaryPicFilenameBase()+"B.txt");
    
      }
    
    #if WCG_WPSNR
      if (!m_pcCfg->getSummaryOutFilename().empty() && useLumaWPSNR)
      {
    
        m_gcAnalyzeWPSNR.printSummary(chFmt, printSequenceMSE, printHexPsnr, bitDepths, m_pcCfg->getSummaryOutFilename());
    
      }
    #endif
      if(isField)
      {
        //-- interlaced summary
        m_gcAnalyzeAll_in.setFrmRate( m_pcCfg->getFrameRate() / (double)m_pcCfg->getTemporalSubsampleRatio());
        m_gcAnalyzeAll_in.setBits(m_gcAnalyzeAll.getBits());
        // prior to the above statement, the interlace analyser does not contain the correct total number of bits.
    
        msg( DETAILS,"\n\nSUMMARY INTERLACED ---------------------------------------------\n" );
    #if ENABLE_QPA
    
        m_gcAnalyzeAll_in.printOut('a', chFmt, printMSEBasedSNR, printSequenceMSE, printHexPsnr, bitDepths, useWPSNR);
    
        m_gcAnalyzeAll_in.printOut('a', chFmt, printMSEBasedSNR, printSequenceMSE, printHexPsnr, bitDepths);
    
    #endif
        if (!m_pcCfg->getSummaryOutFilename().empty())
        {
    
          m_gcAnalyzeAll_in.printSummary(chFmt, printSequenceMSE, printHexPsnr, bitDepths, m_pcCfg->getSummaryOutFilename());
    
            m_gcAnalyzeWPSNR.printSummary(chFmt, printSequenceMSE, printHexPsnr, bitDepths, m_pcCfg->getSummaryOutFilename());
    
          }
    #endif
        }
      }
    
      msg( DETAILS,"\nRVM: %.3lf\n", xCalculateRVM() );
    }
    
    #if W0038_DB_OPT
    uint64_t EncGOP::preLoopFilterPicAndCalcDist( Picture* pcPic )
    {
      CodingStructure& cs = *pcPic->cs;
      m_pcLoopFilter->loopFilterPic( cs );
    
      const CPelUnitBuf picOrg = pcPic->getRecoBuf();
      const CPelUnitBuf picRec = cs.getRecoBuf();
    
      uint64_t uiDist = 0;
      for( uint32_t comp = 0; comp < (uint32_t)picRec.bufs.size(); comp++)
      {
        const ComponentID compID = ComponentID(comp);
        const uint32_t rshift = 2 * DISTORTION_PRECISION_ADJUSTMENT(cs.sps->getBitDepth(toChannelType(compID)));
    #if ENABLE_QPA
        CHECK( rshift >= 8, "shifts greater than 7 are not supported." );
    #endif
        uiDist += xFindDistortionPlane( picOrg.get(compID), picRec.get(compID), rshift );
      }
      return uiDist;
    }
    #endif
    
    // ====================================================================================================================
    // Protected member functions
    // ====================================================================================================================
    
    void EncGOP::xInitGOP( int iPOCLast, int iNumPicRcvd, bool isField
      , bool isEncodeLtRef
    )
    
    {
      CHECK(!( iNumPicRcvd > 0 ), "Unspecified error");
      //  Exception for the first frames
    
      if ((isField && (iPOCLast == 0 || iPOCLast == 1)) || (!isField && (iPOCLast == 0)) || isEncodeLtRef)
    
      {
        m_iGopSize    = 1;
      }
      else
      {
        m_iGopSize    = m_pcCfg->getGOPSize();
      }
      CHECK(!(m_iGopSize > 0), "Unspecified error");
    
      return;
    }
    
    
    void EncGOP::xGetBuffer( PicList&                  rcListPic,
                             std::list<PelUnitBuf*>&   rcListPicYuvRecOut,
                             int                       iNumPicRcvd,
                             int                       iTimeOffset,
                             Picture*&                 rpcPic,
                             int                       pocCurr,
                             bool                      isField )
    {
      int i;
      //  Rec. output
      std::list<PelUnitBuf*>::iterator     iterPicYuvRec = rcListPicYuvRecOut.end();
    
      if (isField && pocCurr > 1 && m_iGopSize!=1)
      {
        iTimeOffset--;
      }
    
    
      int multipleFactor = m_pcCfg->getUseCompositeRef() ? 2 : 1;
      for (i = 0; i < (iNumPicRcvd * multipleFactor - iTimeOffset + 1); i += multipleFactor)
    
      {
        iterPicYuvRec--;
      }
    
      //  Current pic.
      PicList::iterator        iterPic       = rcListPic.begin();
      while (iterPic != rcListPic.end())
      {
        rpcPic = *(iterPic);
        if (rpcPic->getPOC() == pocCurr)
        {
          break;
        }
        iterPic++;
      }
    
      CHECK(!(rpcPic != NULL), "Unspecified error");
      CHECK(!(rpcPic->getPOC() == pocCurr), "Unspecified error");
    
      (**iterPicYuvRec) = rpcPic->getRecoBuf();
      return;
    }
    
    #if ENABLE_QPA
    
    #ifndef BETA
    
      #define BETA 0.5 // value between 0.0 and 1; use 0.0 to obtain traditional PSNR
    
    static inline double calcWeightedSquaredError(const CPelBuf& org,        const CPelBuf& rec,
                                                  double &sumAct,            const uint32_t bitDepth,
                                                  const uint32_t imageWidth, const uint32_t imageHeight,
                                                  const uint32_t offsetX,    const uint32_t offsetY,
                                                  int blockWidth,            int blockHeight)
    
    {
      const int    O = org.stride;
      const int    R = rec.stride;
      const Pel   *o = org.bufAt(offsetX, offsetY);
      const Pel   *r = rec.bufAt(offsetX, offsetY);
      const int yAct = offsetY > 0 ? 0 : 1;
      const int xAct = offsetX > 0 ? 0 : 1;
    
      if (offsetY + (uint32_t)blockHeight > imageHeight) blockHeight = imageHeight - offsetY;
      if (offsetX + (uint32_t)blockWidth  > imageWidth ) blockWidth  = imageWidth  - offsetX;
    
      const int hAct = offsetY + (uint32_t)blockHeight < imageHeight ? blockHeight : blockHeight - 1;