Skip to content
Snippets Groups Projects
EncLib.cpp 62.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • /* The copyright in this software is being made available under the BSD
     * License, included below. This software may be subject to other third party
     * and contributor rights, including patent rights, and no such rights are
     * granted under this license.
     *
    
     * Copyright (c) 2010-2019, ITU/ISO/IEC
    
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions are met:
     *
     *  * Redistributions of source code must retain the above copyright notice,
     *    this list of conditions and the following disclaimer.
     *  * Redistributions in binary form must reproduce the above copyright notice,
     *    this list of conditions and the following disclaimer in the documentation
     *    and/or other materials provided with the distribution.
     *  * Neither the name of the ITU/ISO/IEC nor the names of its contributors may
     *    be used to endorse or promote products derived from this software without
     *    specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
     * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     * THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    /** \file     EncLib.cpp
        \brief    encoder class
    */
    
    #include "EncLib.h"
    
    #include "EncModeCtrl.h"
    #include "AQp.h"
    #include "EncCu.h"
    
    #include "CommonLib/Picture.h"
    #include "CommonLib/CommonDef.h"
    #include "CommonLib/ChromaFormat.h"
    #if ENABLE_SPLIT_PARALLELISM
    #include <omp.h>
    #endif
    
    //! \ingroup EncoderLib
    //! \{
    
    // ====================================================================================================================
    // Constructor / destructor / create / destroy
    // ====================================================================================================================
    
    
    
    EncLib::EncLib()
      : m_spsMap( MAX_NUM_SPS )
      , m_ppsMap( MAX_NUM_PPS )
      , m_AUWriterIf( nullptr )
    #if JVET_J0090_MEMORY_BANDWITH_MEASURE
      , m_cacheModel()
    #endif
    {
      m_iPOCLast          = -1;
      m_iNumPicRcvd       =  0;
      m_uiNumAllPicCoded  =  0;
    
      m_iMaxRefPicNum     = 0;
    
    #if ENABLE_SIMD_OPT_BUFFER
      g_pelBufOP.initPelBufOpsX86();
    #endif
    }
    
    EncLib::~EncLib()
    {
    }
    
    void EncLib::create ()
    {
      // initialize global variables
      initROM();
    
    
    
    
      m_iPOCLast = m_compositeRefEnabled ? -2 : -1;
    
      // create processing unit classes
      m_cGOPEncoder.        create( );
      m_cSliceEncoder.      create( getSourceWidth(), getSourceHeight(), m_chromaFormatIDC, m_maxCUWidth, m_maxCUHeight, m_maxTotalCUDepth );
    #if ENABLE_SPLIT_PARALLELISM || ENABLE_WPP_PARALLELISM
    #if ENABLE_SPLIT_PARALLELISM
      m_numCuEncStacks  = m_numSplitThreads == 1 ? 1 : NUM_RESERVERD_SPLIT_JOBS;
    #else
      m_numCuEncStacks  = 1;
    #endif
    #if ENABLE_WPP_PARALLELISM
      m_numCuEncStacks *= ( m_numWppThreads + m_numWppExtraLines );
    #endif
    
      m_cCuEncoder      = new EncCu              [m_numCuEncStacks];
      m_cInterSearch    = new InterSearch        [m_numCuEncStacks];
      m_cIntraSearch    = new IntraSearch        [m_numCuEncStacks];
      m_cTrQuant        = new TrQuant            [m_numCuEncStacks];
      m_CABACEncoder    = new CABACEncoder       [m_numCuEncStacks];
      m_cRdCost         = new RdCost             [m_numCuEncStacks];
      m_CtxCache        = new CtxCache           [m_numCuEncStacks];
    
      for( int jId = 0; jId < m_numCuEncStacks; jId++ )
      {
        m_cCuEncoder[jId].         create( this );
      }
    #else
      m_cCuEncoder.         create( this );
    #endif
    #if JVET_J0090_MEMORY_BANDWITH_MEASURE
      m_cInterSearch.cacheAssign( &m_cacheModel );
    #endif
      const uint32_t widthInCtus   = (getSourceWidth()  + m_maxCUWidth  - 1)  / m_maxCUWidth;
      const uint32_t heightInCtus  = (getSourceHeight() + m_maxCUHeight - 1) / m_maxCUHeight;
      const uint32_t numCtuInFrame = widthInCtus * heightInCtus;
    
      if (m_bUseSAO)
      {
        m_cEncSAO.create( getSourceWidth(), getSourceHeight(), m_chromaFormatIDC, m_maxCUWidth, m_maxCUHeight, m_maxTotalCUDepth, m_log2SaoOffsetScale[CHANNEL_TYPE_LUMA], m_log2SaoOffsetScale[CHANNEL_TYPE_CHROMA] );
        m_cEncSAO.createEncData(getSaoCtuBoundary(), numCtuInFrame);
      }
    
      m_cLoopFilter.create( m_maxTotalCUDepth );
    
      if( m_alf )
      {
        m_cEncALF.create( getSourceWidth(), getSourceHeight(), m_chromaFormatIDC, m_maxCUWidth, m_maxCUHeight, m_maxTotalCUDepth, m_bitDepth, m_inputBitDepth );
      }
    
      if ( m_RCEnableRateControl )
      {
    
        m_cRateCtrl.init(m_framesToBeEncoded, m_RCTargetBitrate, (int)((double)m_iFrameRate / m_temporalSubsampleRatio + 0.5), m_iGOPSize, m_iSourceWidth, m_iSourceHeight,
          m_maxCUWidth, m_maxCUHeight, getBitDepth(CHANNEL_TYPE_LUMA), m_RCKeepHierarchicalBit, m_RCUseLCUSeparateModel, m_GOPList);
    
      }
    
    }
    
    void EncLib::destroy ()
    {
      // destroy processing unit classes
      m_cGOPEncoder.        destroy();
      m_cSliceEncoder.      destroy();
    #if ENABLE_SPLIT_PARALLELISM || ENABLE_WPP_PARALLELISM
      for( int jId = 0; jId < m_numCuEncStacks; jId++ )
      {
        m_cCuEncoder[jId].destroy();
      }
    #else
      m_cCuEncoder.         destroy();
    #endif
      if( m_alf )
      {
        m_cEncALF.destroy();
      }
      m_cEncSAO.            destroyEncData();
      m_cEncSAO.            destroy();
      m_cLoopFilter.        destroy();
      m_cRateCtrl.          destroy();
    #if ENABLE_SPLIT_PARALLELISM || ENABLE_WPP_PARALLELISM
      for( int jId = 0; jId < m_numCuEncStacks; jId++ )
      {
        m_cInterSearch[jId].   destroy();
        m_cIntraSearch[jId].   destroy();
      }
    #else
      m_cInterSearch.       destroy();
      m_cIntraSearch.       destroy();
    #endif
    
    #if ENABLE_SPLIT_PARALLELISM || ENABLE_WPP_PARALLELISM
      delete[] m_cCuEncoder;
      delete[] m_cInterSearch;
      delete[] m_cIntraSearch;
      delete[] m_cTrQuant;
      delete[] m_CABACEncoder;
      delete[] m_cRdCost;
      delete[] m_CtxCache;
    #endif
    
    
    
    
      // destroy ROM
      destroyROM();
      return;
    }
    
    void EncLib::init( bool isFieldCoding, AUWriterIf* auWriterIf )
    {
      m_AUWriterIf = auWriterIf;
    
      SPS &sps0=*(m_spsMap.allocatePS(0)); // NOTE: implementations that use more than 1 SPS need to be aware of activation issues.
      PPS &pps0=*(m_ppsMap.allocatePS(0));
    
      // initialize SPS
      xInitSPS(sps0);
    #if HEVC_VPS
      xInitVPS(m_cVPS, sps0);
    #endif
    
    #if ENABLE_SPLIT_PARALLELISM
      if( omp_get_dynamic() )
      {
        omp_set_dynamic( false );
      }
      omp_set_nested( true );
    #endif
    
    
      if (sps0.getSpsNext().getUseCompositeRef()) 
      {
        sps0.setLongTermRefsPresent(true);
      }
    
    
    #if U0132_TARGET_BITS_SATURATION
      if (m_RCCpbSaturationEnabled)
      {
        m_cRateCtrl.initHrdParam(sps0.getVuiParameters()->getHrdParameters(), m_iFrameRate, m_RCInitialCpbFullness);
      }
    #endif
    #if ENABLE_SPLIT_PARALLELISM || ENABLE_WPP_PARALLELISM
      for( int jId = 0; jId < m_numCuEncStacks; jId++ )
      {
        m_cRdCost[jId].setCostMode ( m_costMode );
      }
    #else
      m_cRdCost.setCostMode ( m_costMode );
    #endif
    
      // initialize PPS
      xInitPPS(pps0, sps0);
      xInitRPS(sps0, isFieldCoding);
    
    #if ER_CHROMA_QP_WCG_PPS
      if (m_wcgChromaQpControl.isEnabled())
      {
        PPS &pps1=*(m_ppsMap.allocatePS(1));
        xInitPPS(pps1, sps0);
      }
    #endif
    
      if (sps0.getSpsNext().getUseCompositeRef())
      {
        PPS &pps2 = *(m_ppsMap.allocatePS(2));
        xInitPPS(pps2, sps0);
        xInitPPSforLT(pps2);
      }
    
    
      // initialize processing unit classes
      m_cGOPEncoder.  init( this );
      m_cSliceEncoder.init( this, sps0 );
    #if ENABLE_SPLIT_PARALLELISM || ENABLE_WPP_PARALLELISM
      for( int jId = 0; jId < m_numCuEncStacks; jId++ )
      {
        // precache a few objects
        for( int i = 0; i < 10; i++ )
        {
          auto x = m_CtxCache[jId].get();
          m_CtxCache[jId].cache( x );
        }
    
        m_cCuEncoder[jId].init( this, sps0, jId );
    
        // initialize transform & quantization class
        m_cTrQuant[jId].init( jId == 0 ? nullptr : m_cTrQuant[0].getQuant(),
                              1 << m_uiQuadtreeTULog2MaxSize,
                              m_useRDOQ,
                              m_useRDOQTS,
    #if T0196_SELECTIVE_RDOQ
                              m_useSelectiveRDOQ,
    #endif
                              true,
                              m_useTransformSkipFast
        );
    
        // initialize encoder search class
        CABACWriter* cabacEstimator = m_CABACEncoder[jId].getCABACEstimator( &sps0 );
        m_cIntraSearch[jId].init( this,
                                  &m_cTrQuant[jId],
                                  &m_cRdCost[jId],
                                  cabacEstimator,
                                  getCtxCache( jId ), m_maxCUWidth, m_maxCUHeight, m_maxTotalCUDepth );
        m_cInterSearch[jId].init( this,
                                  &m_cTrQuant[jId],
                                  m_iSearchRange,
                                  m_bipredSearchRange,
                                  m_motionEstimationSearchMethod,
                                  m_maxCUWidth, m_maxCUHeight, m_maxTotalCUDepth, &m_cRdCost[jId], cabacEstimator, getCtxCache( jId ) );
    
        // link temporary buffets from intra search with inter search to avoid unnecessary memory overhead
        m_cInterSearch[jId].setTempBuffers( m_cIntraSearch[jId].getSplitCSBuf(), m_cIntraSearch[jId].getFullCSBuf(), m_cIntraSearch[jId].getSaveCSBuf() );
      }
    #else  // ENABLE_SPLIT_PARALLELISM || ENABLE_WPP_PARALLELISM
      m_cCuEncoder.   init( this, sps0 );
    
      // initialize transform & quantization class
      m_cTrQuant.init( nullptr,
                       1 << m_uiQuadtreeTULog2MaxSize,
                       m_useRDOQ,
                       m_useRDOQTS,
    #if T0196_SELECTIVE_RDOQ
                       m_useSelectiveRDOQ,
    #endif
                       true,
                       m_useTransformSkipFast
      );
    
      // initialize encoder search class
      CABACWriter* cabacEstimator = m_CABACEncoder.getCABACEstimator(&sps0);
      m_cIntraSearch.init( this,
                           &m_cTrQuant,
                           &m_cRdCost,
                           cabacEstimator,
                           getCtxCache(), m_maxCUWidth, m_maxCUHeight, m_maxTotalCUDepth );
      m_cInterSearch.init( this,
                           &m_cTrQuant,
                           m_iSearchRange,
                           m_bipredSearchRange,
                           m_motionEstimationSearchMethod,
                           m_maxCUWidth, m_maxCUHeight, m_maxTotalCUDepth, &m_cRdCost, cabacEstimator, getCtxCache() );
    
      // link temporary buffets from intra search with inter search to avoid unneccessary memory overhead
      m_cInterSearch.setTempBuffers( m_cIntraSearch.getSplitCSBuf(), m_cIntraSearch.getFullCSBuf(), m_cIntraSearch.getSaveCSBuf() );
    #endif // ENABLE_SPLIT_PARALLELISM || ENABLE_WPP_PARALLELISM
    
      m_iMaxRefPicNum = 0;
    
    #if HEVC_USE_SCALING_LISTS
    #if ER_CHROMA_QP_WCG_PPS
      if( m_wcgChromaQpControl.isEnabled() )
      {
        xInitScalingLists( sps0, *m_ppsMap.getPS(1) );
        xInitScalingLists( sps0, pps0 );
      }
      else
    #endif
      {
        xInitScalingLists( sps0, pps0 );
      }
    #endif
    #if ENABLE_WPP_PARALLELISM
      m_entropyCodingSyncContextStateVec.resize( pps0.pcv->heightInCtus );
    #endif
    
      if (sps0.getSpsNext().getUseCompositeRef()) 
      {
        Picture *picBg = new Picture;
        picBg->create(sps0.getChromaFormatIdc(), Size(sps0.getPicWidthInLumaSamples(), sps0.getPicHeightInLumaSamples()), sps0.getMaxCUWidth(), sps0.getMaxCUWidth() + 16, false);
        picBg->getRecoBuf().fill(0);
        picBg->finalInit(sps0, pps0);
        picBg->allocateNewSlice();
        picBg->createSpliceIdx(pps0.pcv->sizeInCtus);
        m_cGOPEncoder.setPicBg(picBg);
        Picture *picOrig = new Picture;
        picOrig->create(sps0.getChromaFormatIdc(), Size(sps0.getPicWidthInLumaSamples(), sps0.getPicHeightInLumaSamples()), sps0.getMaxCUWidth(), sps0.getMaxCUWidth() + 16, false);
        picOrig->getOrigBuf().fill(0);
        m_cGOPEncoder.setPicOrig(picOrig);
      }
    
    }
    
    #if HEVC_USE_SCALING_LISTS
    void EncLib::xInitScalingLists(SPS &sps, PPS &pps)
    {
      // Initialise scaling lists
      // The encoder will only use the SPS scaling lists. The PPS will never be marked present.
      const int maxLog2TrDynamicRange[MAX_NUM_CHANNEL_TYPE] =
      {
          sps.getMaxLog2TrDynamicRange(CHANNEL_TYPE_LUMA),
          sps.getMaxLog2TrDynamicRange(CHANNEL_TYPE_CHROMA)
      };
    
      Quant* quant = getTrQuant()->getQuant();
    
      if(getUseScalingListId() == SCALING_LIST_OFF)
      {
        quant->setFlatScalingList(maxLog2TrDynamicRange, sps.getBitDepths());
        quant->setUseScalingList(false);
    #if ENABLE_SPLIT_PARALLELISM || ENABLE_WPP_PARALLELISM
        for( int jId = 1; jId < m_numCuEncStacks; jId++ )
        {
          getTrQuant( jId )->getQuant()->setFlatScalingList( maxLog2TrDynamicRange, sps.getBitDepths() );
          getTrQuant( jId )->getQuant()->setUseScalingList( false );
        }
    #endif
        sps.setScalingListPresentFlag(false);
        pps.setScalingListPresentFlag(false);
      }
      else if(getUseScalingListId() == SCALING_LIST_DEFAULT)
      {
        sps.getScalingList().setDefaultScalingList ();
        sps.setScalingListPresentFlag(false);
        pps.setScalingListPresentFlag(false);
    
        quant->setScalingList(&(sps.getScalingList()), maxLog2TrDynamicRange, sps.getBitDepths());
        quant->setUseScalingList(true);
    #if ENABLE_SPLIT_PARALLELISM || ENABLE_WPP_PARALLELISM
        for( int jId = 1; jId < m_numCuEncStacks; jId++ )
        {
          getTrQuant( jId )->getQuant()->setUseScalingList( true );
        }
    #endif
      }
      else if(getUseScalingListId() == SCALING_LIST_FILE_READ)
      {
        sps.getScalingList().setDefaultScalingList ();
        if(sps.getScalingList().xParseScalingList(getScalingListFileName()))
        {
          THROW( "parse scaling list");
        }
        sps.getScalingList().checkDcOfMatrix();
        sps.setScalingListPresentFlag(sps.getScalingList().checkDefaultScalingList());
        pps.setScalingListPresentFlag(false);
    
        quant->setScalingList(&(sps.getScalingList()), maxLog2TrDynamicRange, sps.getBitDepths());
        quant->setUseScalingList(true);
    #if ENABLE_SPLIT_PARALLELISM || ENABLE_WPP_PARALLELISM
        for( int jId = 1; jId < m_numCuEncStacks; jId++ )
        {
          getTrQuant( jId )->getQuant()->setUseScalingList( true );
        }
    #endif
      }
      else
      {
        THROW("error : ScalingList == " << getUseScalingListId() << " not supported\n");
      }
    
      if (getUseScalingListId() != SCALING_LIST_OFF)
      {
        // Prepare delta's:
        for(uint32_t sizeId = 0; sizeId < SCALING_LIST_SIZE_NUM; sizeId++)
        {
          const int predListStep = (sizeId == SCALING_LIST_32x32? (SCALING_LIST_NUM/NUMBER_OF_PREDICTION_MODES) : 1); // if 32x32, skip over chroma entries.
    
          for(uint32_t listId = 0; listId < SCALING_LIST_NUM; listId+=predListStep)
          {
            sps.getScalingList().checkPredMode( sizeId, listId );
          }
        }
      }
    }
    #endif
    
    
    void EncLib::xInitPPSforLT(PPS& pps)
    {
      pps.setOutputFlagPresentFlag(true);
      pps.setDeblockingFilterControlPresentFlag(true);
      pps.setPPSDeblockingFilterDisabledFlag(true);
    }
    
    
    // ====================================================================================================================
    // Public member functions
    // ====================================================================================================================
    
    void EncLib::deletePicBuffer()
    {
      PicList::iterator iterPic = m_cListPic.begin();
      int iSize = int( m_cListPic.size() );
    
      for ( int i = 0; i < iSize; i++ )
      {
        Picture* pcPic = *(iterPic++);
    
        pcPic->destroy();
    
        // get rid of the qpadaption layer
        while( pcPic->aqlayer.size() )
        {
          delete pcPic->aqlayer.back(); pcPic->aqlayer.pop_back();
        }
    
        delete pcPic;
        pcPic = NULL;
      }
    }
    
    /**
     - Application has picture buffer list with size of GOP + 1
     - Picture buffer list acts like as ring buffer
     - End of the list has the latest picture
     .
     \param   flush               cause encoder to encode a partial GOP
     \param   pcPicYuvOrg         original YUV picture
     \param   pcPicYuvTrueOrg
     \param   snrCSC
     \retval  rcListPicYuvRecOut  list of reconstruction YUV pictures
     \retval  accessUnitsOut      list of output access units
     \retval  iNumEncoded         number of encoded pictures
     */
    void EncLib::encode( bool flush, PelStorage* pcPicYuvOrg, PelStorage* cPicYuvTrueOrg, const InputColourSpaceConversion snrCSC, std::list<PelUnitBuf*>& rcListPicYuvRecOut,
                         int& iNumEncoded )
    {
    
      if (m_compositeRefEnabled && m_cGOPEncoder.getPicBg()->getSpliceFull() && m_iPOCLast >= 10 && m_iNumPicRcvd == 0 && m_cGOPEncoder.getEncodedLTRef() == false)
      {
        Picture* picCurr = NULL;
        xGetNewPicBuffer(rcListPicYuvRecOut, picCurr, 2);
        const PPS *pps = m_ppsMap.getPS(2);
        const SPS *sps = m_spsMap.getPS(pps->getSPSId());
    
        picCurr->M_BUFS(0, PIC_ORIGINAL).copyFrom(m_cGOPEncoder.getPicBg()->getRecoBuf());
        picCurr->finalInit(*sps, *pps);
        picCurr->poc = m_iPOCLast - 1;
        m_iPOCLast -= 2;
        if (getUseAdaptiveQP())
        {
          AQpPreanalyzer::preanalyze(picCurr);
        }
        if (m_RCEnableRateControl)
        {
          m_cRateCtrl.initRCGOP(m_iNumPicRcvd);
        }
        m_cGOPEncoder.compressGOP(m_iPOCLast, m_iNumPicRcvd, m_cListPic, rcListPicYuvRecOut,
          false, false, snrCSC, m_printFrameMSE, true);
        m_cGOPEncoder.setEncodedLTRef(true);
        if (m_RCEnableRateControl)
        {
          m_cRateCtrl.destroyRCGOP();
        }
    
        iNumEncoded = 0;
        m_iNumPicRcvd = 0;
      }
    
      //PROF_ACCUM_AND_START_NEW_SET( getProfilerPic(), P_GOP_LEVEL );
      if (pcPicYuvOrg != NULL)
      {
        // get original YUV
        Picture* pcPicCurr = NULL;
    
    #if ER_CHROMA_QP_WCG_PPS
        int ppsID=-1; // Use default PPS ID
        if (getWCGChromaQPControl().isEnabled())
        {
    
          ppsID = getdQPs()[m_iPOCLast / (m_compositeRefEnabled ? 2 : 1) + 1];
    
          ppsID+=(getSwitchPOC() != -1 && (m_iPOCLast+1 >= getSwitchPOC())?1:0);
        }
        xGetNewPicBuffer( rcListPicYuvRecOut,
                          pcPicCurr, ppsID );
    #else
        xGetNewPicBuffer( rcListPicYuvRecOut,
                          pcPicCurr, -1 ); // Uses default PPS ID. However, could be modified, for example, to use a PPS ID as a function of POC (m_iPOCLast+1)
    #endif
    
        {
          const PPS *pPPS=(ppsID<0) ? m_ppsMap.getFirstPS() : m_ppsMap.getPS(ppsID);
          const SPS *pSPS=m_spsMap.getPS(pPPS->getSPSId());
    
          pcPicCurr->M_BUFS( 0, PIC_ORIGINAL ).swap( *pcPicYuvOrg );
    
          pcPicCurr->finalInit( *pSPS, *pPPS );
        }
    
        pcPicCurr->poc = m_iPOCLast;
    
        // compute image characteristics
        if ( getUseAdaptiveQP() )
        {
          AQpPreanalyzer::preanalyze( pcPicCurr );
        }
      }
    
      if ((m_iNumPicRcvd == 0) || (!flush && (m_iPOCLast != 0) && (m_iNumPicRcvd != m_iGOPSize) && (m_iGOPSize != 0)))
      {
        iNumEncoded = 0;
        return;
      }
    
      if ( m_RCEnableRateControl )
      {
        m_cRateCtrl.initRCGOP( m_iNumPicRcvd );
      }
    
      // compress GOP
    
      m_cGOPEncoder.compressGOP(m_iPOCLast, m_iNumPicRcvd, m_cListPic, rcListPicYuvRecOut,
                                false, false, snrCSC, m_printFrameMSE
        , false
      );
    
    
      if ( m_RCEnableRateControl )
      {
        m_cRateCtrl.destroyRCGOP();
      }
    
      iNumEncoded         = m_iNumPicRcvd;
      m_iNumPicRcvd       = 0;
      m_uiNumAllPicCoded += iNumEncoded;
    }
    
    /**------------------------------------------------
     Separate interlaced frame into two fields
     -------------------------------------------------**/
    void separateFields(Pel* org, Pel* dstField, uint32_t stride, uint32_t width, uint32_t height, bool isTop)
    {
      if (!isTop)
      {
        org += stride;
      }
      for (int y = 0; y < height>>1; y++)
      {
        for (int x = 0; x < width; x++)
        {
          dstField[x] = org[x];
        }
    
        dstField += stride;
        org += stride*2;
      }
    
    }
    
    void EncLib::encode( bool flush, PelStorage* pcPicYuvOrg, PelStorage* pcPicYuvTrueOrg, const InputColourSpaceConversion snrCSC, std::list<PelUnitBuf*>& rcListPicYuvRecOut,
                         int& iNumEncoded, bool isTff )
    {
      iNumEncoded = 0;
    
      for (int fieldNum=0; fieldNum<2; fieldNum++)
      {
        if (pcPicYuvOrg)
        {
          /* -- field initialization -- */
          const bool isTopField=isTff==(fieldNum==0);
    
          Picture *pcField;
          xGetNewPicBuffer( rcListPicYuvRecOut, pcField, -1 );
    
          for (uint32_t comp = 0; comp < ::getNumberValidComponents(pcPicYuvOrg->chromaFormat); comp++)
          {
            const ComponentID compID = ComponentID(comp);
            {
              PelBuf compBuf = pcPicYuvOrg->get( compID );
              separateFields( compBuf.buf,
                             pcField->getOrigBuf().get(compID).buf,
                             compBuf.stride,
                             compBuf.width,
                             compBuf.height,
                             isTopField);
            }
          }
    
          {
            int ppsID=-1; // Use default PPS ID
            const PPS *pPPS=(ppsID<0) ? m_ppsMap.getFirstPS() : m_ppsMap.getPS(ppsID);
            const SPS *pSPS=m_spsMap.getPS(pPPS->getSPSId());
    
            pcField->finalInit( *pSPS, *pPPS );
          }
    
          pcField->poc = m_iPOCLast;
          pcField->reconstructed = false;
    
          pcField->setBorderExtension(false);// where is this normally?
    
          pcField->topField = isTopField;                  // interlaced requirement
    
          // compute image characteristics
          if ( getUseAdaptiveQP() )
          {
            AQpPreanalyzer::preanalyze( pcField );
          }
        }
    
        if ( m_iNumPicRcvd && ((flush&&fieldNum==1) || (m_iPOCLast/2)==0 || m_iNumPicRcvd==m_iGOPSize ) )
        {
          // compress GOP
    
          m_cGOPEncoder.compressGOP(m_iPOCLast, m_iNumPicRcvd, m_cListPic, rcListPicYuvRecOut, true, isTff, snrCSC, m_printFrameMSE
                                  , false
          );
    
    
          iNumEncoded += m_iNumPicRcvd;
          m_uiNumAllPicCoded += m_iNumPicRcvd;
          m_iNumPicRcvd = 0;
        }
      }
    }
    
    
    // ====================================================================================================================
    // Protected member functions
    // ====================================================================================================================
    
    /**
     - Application has picture buffer list with size of GOP + 1
     - Picture buffer list acts like as ring buffer
     - End of the list has the latest picture
     .
     \retval rpcPic obtained picture buffer
     */
    void EncLib::xGetNewPicBuffer ( std::list<PelUnitBuf*>& rcListPicYuvRecOut, Picture*& rpcPic, int ppsId )
    {
      // rotate he output buffer
      rcListPicYuvRecOut.push_back( rcListPicYuvRecOut.front() ); rcListPicYuvRecOut.pop_front();
    
      rpcPic=0;
    
      // At this point, the SPS and PPS can be considered activated - they are copied to the new Pic.
      const PPS *pPPS=(ppsId<0) ? m_ppsMap.getFirstPS() : m_ppsMap.getPS(ppsId);
      CHECK(!(pPPS!=0), "Unspecified error");
      const PPS &pps=*pPPS;
    
      const SPS *pSPS=m_spsMap.getPS(pps.getSPSId());
      CHECK(!(pSPS!=0), "Unspecified error");
      const SPS &sps=*pSPS;
    
      Slice::sortPicList(m_cListPic);
    
      // use an entry in the buffered list if the maximum number that need buffering has been reached:
      if (m_cListPic.size() >= (uint32_t)(m_iGOPSize + getMaxDecPicBuffering(MAX_TLAYER-1) + 2) )
      {
        PicList::iterator iterPic  = m_cListPic.begin();
        int iSize = int( m_cListPic.size() );
        for ( int i = 0; i < iSize; i++ )
        {
          rpcPic = *iterPic;
          if( ! rpcPic->referenced )
          {
            break;
          }
          iterPic++;
        }
    
        // If PPS ID is the same, we will assume that it has not changed since it was last used
        // and return the old object.
        if (pps.getPPSId() != rpcPic->cs->pps->getPPSId())
        {
          // the IDs differ - free up an entry in the list, and then create a new one, as with the case where the max buffering state has not been reached.
          rpcPic->destroy();
          delete rpcPic;
          m_cListPic.erase(iterPic);
          rpcPic=0;
        }
      }
    
      if (rpcPic==0)
      {
        rpcPic = new Picture;
    
        rpcPic->create( sps.getChromaFormatIdc(), Size( sps.getPicWidthInLumaSamples(), sps.getPicHeightInLumaSamples()), sps.getMaxCUWidth(), sps.getMaxCUWidth()+16, false );
        if ( getUseAdaptiveQP() )
        {
          const uint32_t iMaxDQPLayer = pps.getMaxCuDQPDepth()+1;
          rpcPic->aqlayer.resize( iMaxDQPLayer );
          for (uint32_t d = 0; d < iMaxDQPLayer; d++)
          {
            rpcPic->aqlayer[d] = new AQpLayer( sps.getPicWidthInLumaSamples(), sps.getPicHeightInLumaSamples(), sps.getMaxCUWidth()>>d, sps.getMaxCUHeight()>>d );
          }
        }
    
        m_cListPic.push_back( rpcPic );
      }
    
      rpcPic->setBorderExtension( false );
      rpcPic->reconstructed = false;
      rpcPic->referenced = true;
    
    
      m_iPOCLast += (m_compositeRefEnabled ? 2 : 1);
    
      m_iNumPicRcvd++;
    }
    
    
    #if HEVC_VPS
    void EncLib::xInitVPS(VPS &vps, const SPS &sps)
    {
      // The SPS must have already been set up.
      // set the VPS profile information.
      *vps.getPTL() = *sps.getPTL();
      vps.setMaxOpSets(1);
      vps.getTimingInfo()->setTimingInfoPresentFlag       ( false );
      vps.setNumHrdParameters( 0 );
    
      vps.createHrdParamBuffer();
      for( uint32_t i = 0; i < vps.getNumHrdParameters(); i ++ )
      {
        vps.setHrdOpSetIdx( 0, i );
        vps.setCprmsPresentFlag( false, i );
        // Set up HrdParameters here.
      }
    }
    #endif
    
    void EncLib::xInitSPS(SPS &sps)
    {
    
      sps.setIntraOnlyConstraintFlag(m_intraConstraintFlag);
      sps.setMaxBitDepthConstraintIdc(m_bitDepthConstraintValue - 8);
      sps.setMaxChromaFormatConstraintIdc(m_chromaFormatConstraintValue);
      sps.setFrameConstraintFlag(m_frameOnlyConstraintFlag);
      sps.setNoQtbttDualTreeIntraConstraintFlag(!m_dualITree);
      sps.setNoCclmConstraintFlag(m_LMChroma ? false : true);
      sps.setNoSaoConstraintFlag(!m_bUseSAO);
      sps.setNoAlfConstraintFlag(!m_alf);
      sps.setNoPcmConstraintFlag(!m_usePCM);
      sps.setNoTemporalMvpConstraintFlag(m_TMVPModeId ? false : true);
      sps.setNoSbtmvpConstraintFlag(m_SubPuMvpMode ? false : true);
      sps.setNoAmvrConstraintFlag(!m_bNoAmvrConstraintFlag);
      sps.setNoAffineMotionConstraintFlag(!m_Affine);
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #if JVET_M0464_UNI_MTS
      sps.setNoMtsConstraintFlag((m_IntraMTS || m_InterMTS) ? false : true);
    #else
    
      sps.setNoMtsConstraintFlag((m_IntraEMT || m_InterEMT) ? false : true);
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #endif
    
      sps.setNoLadfConstraintFlag(!m_LadfEnabled);
      sps.setNoDepQuantConstraintFlag(!m_DepQuantEnabledFlag);
      sps.setNoSignDataHidingConstraintFlag(!m_SignDataHidingEnabledFlag);
    
      ProfileTierLevel& profileTierLevel = *sps.getPTL()->getGeneralPTL();
      profileTierLevel.setLevelIdc                    (m_level);
      profileTierLevel.setTierFlag                    (m_levelTier);
      profileTierLevel.setProfileIdc                  (m_profile);
      profileTierLevel.setProfileCompatibilityFlag    (m_profile, 1);
      profileTierLevel.setProgressiveSourceFlag       (m_progressiveSourceFlag);
      profileTierLevel.setInterlacedSourceFlag        (m_interlacedSourceFlag);
      profileTierLevel.setNonPackedConstraintFlag     (m_nonPackedConstraintFlag);
      profileTierLevel.setFrameOnlyConstraintFlag     (m_frameOnlyConstraintFlag);
      profileTierLevel.setBitDepthConstraint          (m_bitDepthConstraintValue);
      profileTierLevel.setChromaFormatConstraint      (m_chromaFormatConstraintValue);
      profileTierLevel.setIntraConstraintFlag         (m_intraConstraintFlag);
      profileTierLevel.setOnePictureOnlyConstraintFlag(m_onePictureOnlyConstraintFlag);
      profileTierLevel.setLowerBitRateConstraintFlag  (m_lowerBitRateConstraintFlag);
    
      if ((m_profile == Profile::MAIN10) && (m_bitDepth[CHANNEL_TYPE_LUMA] == 8) && (m_bitDepth[CHANNEL_TYPE_CHROMA] == 8))
      {
        /* The above constraint is equal to Profile::MAIN */
        profileTierLevel.setProfileCompatibilityFlag(Profile::MAIN, 1);
      }
      if (m_profile == Profile::MAIN)
      {
        /* A Profile::MAIN10 decoder can always decode Profile::MAIN */
        profileTierLevel.setProfileCompatibilityFlag( Profile::MAIN10, 1 );
      }
    
      /* XXX: should Main be marked as compatible with still picture? */
      /* XXX: may be a good idea to refactor the above into a function
       * that chooses the actual compatibility based upon options */
    
      sps.setPicWidthInLumaSamples  ( m_iSourceWidth      );
      sps.setPicHeightInLumaSamples ( m_iSourceHeight     );
      sps.setConformanceWindow      ( m_conformanceWindow );
      sps.setMaxCUWidth             ( m_maxCUWidth        );
      sps.setMaxCUHeight            ( m_maxCUHeight       );
      sps.setMaxCodingDepth         ( m_maxTotalCUDepth   );
      sps.setChromaFormatIdc        ( m_chromaFormatIDC   );
      sps.setLog2DiffMaxMinCodingBlockSize(m_log2DiffMaxMinCodingBlockSize);
    
      sps.getSpsNext().setNextToolsEnabled      ( m_profile == Profile::NEXT );
    
      sps.setCTUSize                             ( m_CTUSize );
      sps.setSplitConsOverrideEnabledFlag        ( m_useSplitConsOverride );
      sps.setMinQTSizes                          ( m_uiMinQT );
      sps.getSpsNext().setUseLargeCTU            ( m_LargeCTU );
      sps.setMaxBTDepth                          ( m_uiMaxBTDepth, m_uiMaxBTDepthI, m_uiMaxBTDepthIChroma );
      sps.setUseDualITree                        ( m_dualITree );
    
      sps.setSBTMVPEnabledFlag                  ( m_SubPuMvpMode );
    
      sps.getSpsNext().setImvMode               ( ImvMode(m_ImvMode) );
      sps.getSpsNext().setUseIMV                ( m_ImvMode != IMV_OFF );
    
      sps.setBDOFEnabledFlag                    ( m_BIO );
    
      sps.getSpsNext().setUseAffine             ( m_Affine );
      sps.getSpsNext().setUseAffineType         ( m_AffineType );
      sps.getSpsNext().setDisableMotCompress    ( m_DisableMotionCompression );
      sps.getSpsNext().setMTTMode               ( m_MTTMode );
      sps.getSpsNext().setUseLMChroma           ( m_LMChroma ? true : false );
    
    Philippe Hanhart's avatar
    Philippe Hanhart committed
    #if JVET_M0142_CCLM_COLLOCATED_CHROMA
      sps.getSpsNext().setCclmCollocatedChromaFlag( m_cclmCollocatedChromaFlag );
    #endif
    
    #if ENABLE_WPP_PARALLELISM
      sps.getSpsNext().setUseNextDQP            ( m_AltDQPCoding );
    #endif
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #if JVET_M0464_UNI_MTS
      sps.getSpsNext().setUseIntraMTS           ( m_IntraMTS );
      sps.getSpsNext().setUseInterMTS           ( m_InterMTS );
    #else
    
      sps.getSpsNext().setUseIntraEMT           ( m_IntraEMT );
      sps.getSpsNext().setUseInterEMT           ( m_InterEMT );
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #endif
    
      sps.getSpsNext().setUseCompositeRef       ( m_compositeRefEnabled );
    
      sps.getSpsNext().setUseGBi                ( m_GBi );
    
    #if LUMA_ADAPTIVE_DEBLOCKING_FILTER_QP_OFFSET
      sps.getSpsNext().setLadfEnabled           ( m_LadfEnabled );
      if ( m_LadfEnabled )
      {
        sps.getSpsNext().setLadfNumIntervals    ( m_LadfNumIntervals );
        for ( int k = 0; k < m_LadfNumIntervals; k++ )
        {
          sps.getSpsNext().setLadfQpOffset( m_LadfQpOffset[k], k );
          sps.getSpsNext().setLadfIntervalLowerBound( m_LadfIntervalLowerBound[k], k );
        }
        CHECK( m_LadfIntervalLowerBound[0] != 0, "abnormal value set to LadfIntervalLowerBound[0]" );
      }
    #endif
    
    
      sps.getSpsNext().setUseMHIntra            ( m_MHIntra );
    
      sps.getSpsNext().setUseTriangle           ( m_Triangle );
    
    #if JVET_M0255_FRACMMVD_SWITCH
    
      sps.setDisFracMmvdEnabledFlag             ( m_allowDisFracMMVD );
    
    #endif
    #if JVET_M0246_AFFINE_AMVR
      sps.setAffineAmvrEnabledFlag              ( m_AffineAmvr );
    
    Yu Han's avatar
    Yu Han committed
      sps.getSpsNext().setIBCMode               ( m_IBCMode );
    
    Philippe Hanhart's avatar
    Philippe Hanhart committed
    
    
      sps.setWrapAroundEnabledFlag                      ( m_wrapAround );
    
      sps.setWrapAroundOffset                   ( m_wrapAroundOffset );
    
      // ADD_NEW_TOOL : (encoder lib) set tool enabling flags and associated parameters here
    
    
    Karsten Suehring's avatar
    Karsten Suehring committed
      int minCUSize =  sps.getMaxCUWidth() >> sps.getLog2DiffMaxMinCodingBlockSize();
    
      int log2MinCUSize = 0;
      while(minCUSize > 1)
      {
        minCUSize >>= 1;
        log2MinCUSize++;
      }
    
      sps.setLog2MinCodingBlockSize(log2MinCUSize);
    
      sps.setPCMLog2MinSize (m_uiPCMLog2MinSize);
    
      sps.setPCMEnabledFlag        ( m_usePCM           );
    
      sps.setPCMLog2MaxSize( m_pcmLog2MaxSize  );
    
      sps.setQuadtreeTULog2MaxSize( m_uiQuadtreeTULog2MaxSize );
      sps.setQuadtreeTULog2MinSize( m_uiQuadtreeTULog2MinSize );
      sps.setQuadtreeTUMaxDepthInter( m_uiQuadtreeTUMaxDepthInter    );
      sps.setQuadtreeTUMaxDepthIntra( m_uiQuadtreeTUMaxDepthIntra    );
    
      sps.setSPSTemporalMVPEnabledFlag((getTMVPModeId() == 2 || getTMVPModeId() == 1));
    
      sps.setMaxTrSize   ( 1 << m_uiQuadtreeTULog2MaxSize );
    
      for (uint32_t channelType = 0; channelType < MAX_NUM_CHANNEL_TYPE; channelType++)
      {
        sps.setBitDepth      (ChannelType(channelType), m_bitDepth[channelType] );
        sps.setQpBDOffset  (ChannelType(channelType), (6 * (m_bitDepth[channelType] - 8)));
        sps.setPCMBitDepth (ChannelType(channelType), m_PCMBitDepth[channelType]         );
      }
    
    
      sps.setSAOEnabledFlag( m_bUseSAO );
    
    
      sps.setMaxTLayers( m_maxTempLayer );
      sps.setTemporalIdNestingFlag( ( m_maxTempLayer == 1 ) ? true : false );
    
    
      for (int i = 0; i < std::min(sps.getMaxTLayers(), (uint32_t) MAX_TLAYER); i++ )
    
      {
        sps.setMaxDecPicBuffering(m_maxDecPicBuffering[i], i);
        sps.setNumReorderPics(m_numReorderPics[i], i);
      }
    
      sps.setPCMFilterDisableFlag  ( m_bPCMFilterDisableFlag );
    #if HEVC_USE_SCALING_LISTS
      sps.setScalingListFlag ( (m_useScalingListId == SCALING_LIST_OFF) ? 0 : 1 );
    #endif
    #if HEVC_USE_INTRA_SMOOTHING_T32 || HEVC_USE_INTRA_SMOOTHING_T64
      sps.setUseStrongIntraSmoothing( m_useStrongIntraSmoothing );
    #endif
    
      sps.setALFEnabledFlag( m_alf );
    
      sps.setVuiParametersPresentFlag(getVuiParametersPresentFlag());
    
      if (sps.getVuiParametersPresentFlag())
      {
        VUI* pcVUI = sps.getVuiParameters();
        pcVUI->setAspectRatioInfoPresentFlag(getAspectRatioInfoPresentFlag());
        pcVUI->setAspectRatioIdc(getAspectRatioIdc());
        pcVUI->setSarWidth(getSarWidth());
        pcVUI->setSarHeight(getSarHeight());
        pcVUI->setOverscanInfoPresentFlag(getOverscanInfoPresentFlag());
        pcVUI->setOverscanAppropriateFlag(getOverscanAppropriateFlag());
        pcVUI->setVideoSignalTypePresentFlag(getVideoSignalTypePresentFlag());
        pcVUI->setVideoFormat(getVideoFormat());
        pcVUI->setVideoFullRangeFlag(getVideoFullRangeFlag());
        pcVUI->setColourDescriptionPresentFlag(getColourDescriptionPresentFlag());
        pcVUI->setColourPrimaries(getColourPrimaries());
        pcVUI->setTransferCharacteristics(getTransferCharacteristics());
        pcVUI->setMatrixCoefficients(getMatrixCoefficients());
        pcVUI->setChromaLocInfoPresentFlag(getChromaLocInfoPresentFlag());
        pcVUI->setChromaSampleLocTypeTopField(getChromaSampleLocTypeTopField());
        pcVUI->setChromaSampleLocTypeBottomField(getChromaSampleLocTypeBottomField());
        pcVUI->setNeutralChromaIndicationFlag(getNeutralChromaIndicationFlag());
        pcVUI->setDefaultDisplayWindow(getDefaultDisplayWindow());
        pcVUI->setFrameFieldInfoPresentFlag(getFrameFieldInfoPresentFlag());
        pcVUI->setFieldSeqFlag(false);
        pcVUI->setHrdParametersPresentFlag(false);
        pcVUI->getTimingInfo()->setPocProportionalToTimingFlag(getPocProportionalToTimingFlag());
        pcVUI->getTimingInfo()->setNumTicksPocDiffOneMinus1   (getNumTicksPocDiffOneMinus1()   );
        pcVUI->setBitstreamRestrictionFlag(getBitstreamRestrictionFlag());
    #if HEVC_TILES_WPP
        pcVUI->setTilesFixedStructureFlag(getTilesFixedStructureFlag());
    #endif
        pcVUI->setMotionVectorsOverPicBoundariesFlag(getMotionVectorsOverPicBoundariesFlag());
        pcVUI->setMinSpatialSegmentationIdc(getMinSpatialSegmentationIdc());
        pcVUI->setMaxBytesPerPicDenom(getMaxBytesPerPicDenom());
        pcVUI->setMaxBitsPerMinCuDenom(getMaxBitsPerMinCuDenom());
        pcVUI->setLog2MaxMvLengthHorizontal(getLog2MaxMvLengthHorizontal());
        pcVUI->setLog2MaxMvLengthVertical(getLog2MaxMvLengthVertical());
      }
    
      sps.setNumLongTermRefPicSPS(NUM_LONG_TERM_REF_PIC_SPS);
      CHECK(!(NUM_LONG_TERM_REF_PIC_SPS <= MAX_NUM_LONG_TERM_REF_PICS), "Unspecified error");
      for (int k = 0; k < NUM_LONG_TERM_REF_PIC_SPS; k++)
      {
        sps.setLtRefPicPocLsbSps(k, 0);
        sps.setUsedByCurrPicLtSPSFlag(k, 0);