Skip to content
Snippets Groups Projects
EncGOP.cpp 293 KiB
Newer Older
/* 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-2023, 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     EncGOP.cpp
    \brief    GOP encoder class
*/

#include <list>
#include <algorithm>
#include <functional>

#include "EncLib.h"
#include "EncGOP.h"
#include "Analyze.h"
#include "libmd5/MD5.h"
#include "CommonLib/SEI.h"
#include "CommonLib/NAL.h"
#include "NALwrite.h"

#include <math.h>
#include <deque>
#include <chrono>
#include <cinttypes>

#include "CommonLib/UnitTools.h"
#include "CommonLib/dtrace_codingstruct.h"
#include "CommonLib/dtrace_buffer.h"
#include "CommonLib/ProfileLevelTier.h"

#include "DecoderLib/DecLib.h"

#define ENCODE_SUB_SET 0

using namespace std;

//! \ingroup EncoderLib
//! \{

// ====================================================================================================================
// Constructor / destructor / initialization / destroy
// ====================================================================================================================
int getLSB(int poc, int maxLSB)
{
  if (poc >= 0)
  {
    return poc % maxLSB;
  }
  else
  {
    return (maxLSB - ((-poc) % maxLSB)) % maxLSB;
  }
}

EncGOP::EncGOP()
{
  m_iLastIDR            = 0;
  m_iGopSize            = 0;
  m_iNumPicCoded        = 0; //Niko
  m_bFirst              = true;
  m_iLastRecoveryPicPOC = 0;
  m_latestDRAPPOC       = MAX_INT;
  m_lastRasPoc          = MAX_INT;

  m_pcCfg               = NULL;
  m_pcSliceEncoder      = NULL;
  m_pcListPic           = NULL;
  m_HLSWriter           = NULL;
  m_bSeqFirst           = true;

  m_bRefreshPending     = 0;
  m_pocCRA              = 0;
  m_numLongTermRefPicSPS = 0;
  ::memset(m_ltRefPicPocLsbSps, 0, sizeof(m_ltRefPicPocLsbSps));
  ::memset(m_ltRefPicUsedByCurrPicFlag, 0, sizeof(m_ltRefPicUsedByCurrPicFlag));
  ::memset(m_lastBPSEI, 0, sizeof(m_lastBPSEI));
  m_rapWithLeading      = false;
  for (int i = 0; i < MAX_VPS_LAYERS; i++)
  {
    m_associatedIRAPType[i] = NAL_UNIT_CODED_SLICE_IDR_N_LP;
  }
  ::memset(m_associatedIRAPPOC, 0, sizeof(m_associatedIRAPPOC));
#if W0038_DB_OPT
  m_pcDeblockingTempPicYuv = NULL;
#endif

#if JVET_O0756_CALCULATE_HDRMETRICS
  m_ppcFrameOrg             = nullptr;
  m_ppcFrameRec             = nullptr;
  m_pcConvertFormat         = nullptr;
  m_pcConvertIQuantize      = nullptr;
  m_pcColorTransform        = nullptr;
  m_pcDistortionDeltaE      = nullptr;
  m_pcTransferFct           = nullptr;
  m_pcColorTransformParams  = nullptr;
  m_pcFrameFormat           = nullptr;
  m_metricTime = std::chrono::milliseconds(0);
#endif
  m_bgPOC = -1;
  m_picBg = NULL;
  m_picOrig = NULL;
  m_isEncodedLTRef = false;
  m_isUseLTRef = false;
  m_isPrepareLTRef = true;
  m_lastLTRefPoc = 0;
}

EncGOP::~EncGOP()
{
  if( !m_pcCfg->getDecodeBitstream(0).empty() || !m_pcCfg->getDecodeBitstream(1).empty() )
  {
    // reset potential decoder resources
    tryDecodePicture( NULL, 0, std::string("") );
  }
#if JVET_O0756_CALCULATE_HDRMETRICS
  delete [] m_ppcFrameOrg;
  delete [] m_ppcFrameRec;
  m_ppcFrameOrg = m_ppcFrameRec = nullptr;
  delete m_pcConvertFormat;
  delete m_pcConvertIQuantize;
  delete m_pcColorTransform;
  delete m_pcDistortionDeltaE;
  delete m_pcTransferFct;
  delete m_pcColorTransformParams;
  delete m_pcFrameFormat;
  m_pcConvertFormat         = nullptr;
  m_pcConvertIQuantize      = nullptr;
  m_pcColorTransform        = nullptr;
  m_pcDistortionDeltaE      = nullptr;
  m_pcTransferFct           = nullptr;
  m_pcColorTransformParams  = nullptr;
  m_pcFrameFormat           = nullptr;
}

/** Create list to contain pointers to CTU start addresses of slice.
 */
void  EncGOP::create()
{
  m_bLongtermTestPictureHasBeenCoded = 0;
  m_bLongtermTestPictureHasBeenCoded2 = 0;
#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
Vadim Seregin's avatar
Vadim Seregin committed
  m_cBilateralFilter.create();
#endif
}

void  EncGOP::destroy()
{
#if W0038_DB_OPT
  if (m_pcDeblockingTempPicYuv)
  {
    m_pcDeblockingTempPicYuv->destroy();
    delete m_pcDeblockingTempPicYuv;
    m_pcDeblockingTempPicYuv = NULL;
  }
#endif
  if (m_picBg)
  {
    m_picBg->destroy();
    delete m_picBg;
    m_picBg = NULL;
  }
  if (m_picOrig)
  {
    m_picOrig->destroy();
    delete m_picOrig;
    m_picOrig = NULL;
  }
#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
Vadim Seregin's avatar
Vadim Seregin committed
  m_cBilateralFilter.destroy();
#endif
}

void EncGOP::init ( EncLib* pcEncLib )
{
  m_pcEncLib     = pcEncLib;
  m_pcCfg                = pcEncLib;
  m_seiEncoder.init(m_pcCfg, pcEncLib, this);
  m_pcSliceEncoder       = pcEncLib->getSliceEncoder();
  m_pcListPic            = pcEncLib->getListPic();
  m_HLSWriter            = pcEncLib->getHLSWriter();
  m_pcLoopFilter         = pcEncLib->getLoopFilter();
  m_pcSAO                = pcEncLib->getSAO();
  m_pcALF                = pcEncLib->getALF();
#if JVET_AA0096_MC_BOUNDARY_PADDING
  m_pcFrameMcPadPrediction = pcEncLib->getFrameMcPadPredSearch();
#endif
  ::memset(m_lastBPSEI, 0, sizeof(m_lastBPSEI));
  ::memset(m_totalCoded, 0, sizeof(m_totalCoded));
  if (m_pcCfg->getLmcs())
  {
    pcEncLib->getRdCost()->setReshapeInfo(m_pcCfg->getReshapeSignalType(), m_pcCfg->getBitDepth(CHANNEL_TYPE_LUMA));
    pcEncLib->getRdCost()->initLumaLevelToWeightTableReshape();
  }
  else if (m_pcCfg->getLumaLevelToDeltaQPMapping().mode)
  {
Taoran Lu's avatar
Taoran Lu committed
    pcEncLib->getRdCost()->setReshapeInfo(RESHAPE_SIGNAL_PQ, m_pcCfg->getBitDepth(CHANNEL_TYPE_LUMA));
    pcEncLib->getRdCost()->initLumaLevelToWeightTableReshape();
  }
  pcEncLib->getALF()->getLumaLevelWeightTable() = pcEncLib->getRdCost()->getLumaLevelWeightTable();
Taoran Lu's avatar
Taoran Lu committed
  int alfWSSD = 0;
  if (m_pcCfg->getLmcs() && m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_PQ )
Taoran Lu's avatar
Taoran Lu committed
  {
    alfWSSD = 1;
  }
  pcEncLib->getALF()->setAlfWSSD(alfWSSD);
Taoran Lu's avatar
Taoran Lu committed
#endif
  m_pcReshaper = pcEncLib->getReshaper();
Vadim Seregin's avatar
Vadim Seregin committed

#if JVET_O0756_CALCULATE_HDRMETRICS
  const bool calculateHdrMetrics = m_pcEncLib->getCalcluateHdrMetrics();
  if(calculateHdrMetrics)
  {
    //allocate frame buffers and initialize class members
    int chainNumber = 5;
Vadim Seregin's avatar
Vadim Seregin committed

    m_ppcFrameOrg = new hdrtoolslib::Frame* [chainNumber];
    m_ppcFrameRec = new hdrtoolslib::Frame* [chainNumber];
Vadim Seregin's avatar
Vadim Seregin committed

    double* whitePointDeltaE = new double[hdrtoolslib::NB_REF_WHITE];
    for (int i=0; i<hdrtoolslib::NB_REF_WHITE; i++)
    {
      whitePointDeltaE[i] = m_pcCfg->getWhitePointDeltaE(i);
    }
Vadim Seregin's avatar
Vadim Seregin committed

    double maxSampleValue                       = m_pcCfg->getMaxSampleValue();
    hdrtoolslib::SampleRange sampleRange        = m_pcCfg->getSampleRange();
    hdrtoolslib::ChromaFormat chFmt             = hdrtoolslib::ChromaFormat(m_pcCfg->getChromaFormatIdc());
    int bitDepth = m_pcCfg->getBitDepth(CHANNEL_TYPE_LUMA);
    hdrtoolslib::ColorPrimaries colorPrimaries  = m_pcCfg->getColorPrimaries();
    bool enableTFunctionLUT                     = m_pcCfg->getEnableTFunctionLUT();
    hdrtoolslib::ChromaLocation* chromaLocation = new hdrtoolslib::ChromaLocation[2];
    for (int i=0; i<2; i++)
    {
      chromaLocation[i] = m_pcCfg->getChromaLocation(i);
    }
    int chromaUpFilter  = m_pcCfg->getChromaUPFilter();
    int cropOffsetLeft   = m_pcCfg->getCropOffsetLeft();
    int cropOffsetTop    = m_pcCfg->getCropOffsetTop();
    int cropOffsetRight  = m_pcCfg->getCropOffsetRight();
    int cropOffsetBottom = m_pcCfg->getCropOffsetBottom();
Vadim Seregin's avatar
Vadim Seregin committed

    int width  = m_pcCfg->getSourceWidth() - cropOffsetLeft + cropOffsetRight;
    int height = m_pcCfg->getSourceHeight() - cropOffsetTop  + cropOffsetBottom;
Vadim Seregin's avatar
Vadim Seregin committed

    m_ppcFrameOrg[0] = new hdrtoolslib::Frame(width, height, false, hdrtoolslib::CM_YCbCr, colorPrimaries, chFmt, sampleRange, bitDepth, false, hdrtoolslib::TF_PQ, 0);
    m_ppcFrameRec[0] = new hdrtoolslib::Frame(width, height, false, hdrtoolslib::CM_YCbCr, colorPrimaries, chFmt, sampleRange, bitDepth, false, hdrtoolslib::TF_PQ, 0);
Vadim Seregin's avatar
Vadim Seregin committed

    m_ppcFrameOrg[1] = new hdrtoolslib::Frame(m_ppcFrameOrg[0]->m_width[hdrtoolslib::Y_COMP], m_ppcFrameOrg[0]->m_height[hdrtoolslib::Y_COMP], false, hdrtoolslib::CM_YCbCr, colorPrimaries, hdrtoolslib::CF_444, sampleRange, bitDepth, false, hdrtoolslib::TF_PQ, 0);
    m_ppcFrameRec[1] = new hdrtoolslib::Frame(m_ppcFrameRec[0]->m_width[hdrtoolslib::Y_COMP], m_ppcFrameRec[0]->m_height[hdrtoolslib::Y_COMP], false, hdrtoolslib::CM_YCbCr, colorPrimaries, hdrtoolslib::CF_444, sampleRange, bitDepth, false, hdrtoolslib::TF_PQ, 0);                                // 420 to 444 conversion
Vadim Seregin's avatar
Vadim Seregin committed

    m_ppcFrameOrg[2] =  new hdrtoolslib::Frame(m_ppcFrameOrg[0]->m_width[hdrtoolslib::Y_COMP], m_ppcFrameOrg[0]->m_height[hdrtoolslib::Y_COMP], true, hdrtoolslib::CM_YCbCr, colorPrimaries, hdrtoolslib::CF_444, hdrtoolslib::SR_UNKNOWN, 32, false, hdrtoolslib::TF_PQ, 0);
    m_ppcFrameRec[2] =  new hdrtoolslib::Frame(m_ppcFrameRec[0]->m_width[hdrtoolslib::Y_COMP], m_ppcFrameRec[0]->m_height[hdrtoolslib::Y_COMP], true, hdrtoolslib::CM_YCbCr, colorPrimaries, hdrtoolslib::CF_444, hdrtoolslib::SR_UNKNOWN, 32, false, hdrtoolslib::TF_PQ, 0);                                // 444 to Float conversion
Vadim Seregin's avatar
Vadim Seregin committed

    m_ppcFrameOrg[3] = new hdrtoolslib::Frame(m_ppcFrameOrg[0]->m_width[hdrtoolslib::Y_COMP], m_ppcFrameOrg[0]->m_height[hdrtoolslib::Y_COMP], true, hdrtoolslib::CM_RGB, hdrtoolslib::CP_2020, hdrtoolslib::CF_444, hdrtoolslib::SR_UNKNOWN, 32, false, hdrtoolslib::TF_PQ, 0);
    m_ppcFrameRec[3] = new hdrtoolslib::Frame(m_ppcFrameRec[0]->m_width[hdrtoolslib::Y_COMP], m_ppcFrameRec[0]->m_height[hdrtoolslib::Y_COMP], true, hdrtoolslib::CM_RGB, hdrtoolslib::CP_2020, hdrtoolslib::CF_444, hdrtoolslib::SR_UNKNOWN, 32, false, hdrtoolslib::TF_PQ, 0);                                // YCbCr to RGB conversion
Vadim Seregin's avatar
Vadim Seregin committed

    m_ppcFrameOrg[4] = new hdrtoolslib::Frame(m_ppcFrameOrg[0]->m_width[hdrtoolslib::Y_COMP], m_ppcFrameOrg[0]->m_height[hdrtoolslib::Y_COMP], true, hdrtoolslib::CM_RGB, hdrtoolslib::CP_2020, hdrtoolslib::CF_444, hdrtoolslib::SR_UNKNOWN, 32, false, hdrtoolslib::TF_NULL, 0);
    m_ppcFrameRec[4] = new hdrtoolslib::Frame(m_ppcFrameRec[0]->m_width[hdrtoolslib::Y_COMP], m_ppcFrameRec[0]->m_height[hdrtoolslib::Y_COMP], true, hdrtoolslib::CM_RGB, hdrtoolslib::CP_2020, hdrtoolslib::CF_444, hdrtoolslib::SR_UNKNOWN, 32, false, hdrtoolslib::TF_NULL, 0);                                // Inverse Transfer Function
Vadim Seregin's avatar
Vadim Seregin committed

    m_pcFrameFormat                   = new hdrtoolslib::FrameFormat();
    m_pcFrameFormat->m_isFloat        = true;
    m_pcFrameFormat->m_chromaFormat   = hdrtoolslib::CF_UNKNOWN;
    m_pcFrameFormat->m_colorSpace     = hdrtoolslib::CM_RGB;
    m_pcFrameFormat->m_colorPrimaries = hdrtoolslib::CP_2020;
    m_pcFrameFormat->m_sampleRange    = hdrtoolslib::SR_UNKNOWN;
Vadim Seregin's avatar
Vadim Seregin committed

    m_pcConvertFormat     = hdrtoolslib::ConvertColorFormat::create(width, height, chFmt, hdrtoolslib::CF_444, chromaUpFilter, chromaLocation, chromaLocation);
    m_pcConvertIQuantize  = hdrtoolslib::Convert::create(&m_ppcFrameOrg[1]->m_format, &m_ppcFrameOrg[2]->m_format);
    m_pcColorTransform    = hdrtoolslib::ColorTransform::create(m_ppcFrameOrg[2]->m_colorSpace, m_ppcFrameOrg[2]->m_colorPrimaries, m_ppcFrameOrg[3]->m_colorSpace, m_ppcFrameOrg[3]->m_colorPrimaries, true, 1);
    m_pcDistortionDeltaE  = new hdrtoolslib::DistortionMetricDeltaE(m_pcFrameFormat, false, maxSampleValue, whitePointDeltaE, 1);
    m_pcTransferFct       = hdrtoolslib::TransferFunction::create(hdrtoolslib::TF_PQ, true, (float) maxSampleValue, 0, 0.0, 1.0, enableTFunctionLUT);
Seungwook Hong's avatar
Seungwook Hong committed
#if JVET_Z0118_GDR
  m_lastGdrIntervalPoc = -1;
#endif
}

int EncGOP::xWriteVPS (AccessUnit &accessUnit, const VPS *vps)
{
  OutputNALUnit nalu(NAL_UNIT_VPS);
  m_HLSWriter->setBitstream( &nalu.m_Bitstream );
  CHECK( nalu.m_temporalId, "The value of TemporalId of VPS NAL units shall be equal to 0" );
  m_HLSWriter->codeVPS( vps );
  accessUnit.push_back(new NALUnitEBSP(nalu));
  return (int)(accessUnit.back()->m_nalUnitData.str().size()) * 8;
}
int EncGOP::xWriteDCI(AccessUnit& accessUnit, const DCI* dci)
{
  OutputNALUnit nalu(NAL_UNIT_DCI);
  m_HLSWriter->setBitstream(&nalu.m_Bitstream);
  CHECK(nalu.m_temporalId, "The value of TemporalId of DCI NAL units shall be equal to 0");
  m_HLSWriter->codeDCI(dci);
  accessUnit.push_back(new NALUnitEBSP(nalu));
  return (int)(accessUnit.back()->m_nalUnitData.str().size()) * 8;
}
int EncGOP::xWriteSPS( AccessUnit &accessUnit, const SPS *sps, const int layerId )
{
  OutputNALUnit nalu(NAL_UNIT_SPS);
  m_HLSWriter->setBitstream( &nalu.m_Bitstream );
  nalu.m_nuhLayerId = layerId;
Vadim Seregin's avatar
Vadim Seregin committed
  CHECK( nalu.m_temporalId, "The value of TemporalId of SPS NAL units shall be equal to 0" );
  m_HLSWriter->codeSPS( sps );
  accessUnit.push_back(new NALUnitEBSP(nalu));
  return (int)(accessUnit.back()->m_nalUnitData.str().size()) * 8;

}

int EncGOP::xWritePPS( AccessUnit &accessUnit, const PPS *pps, const int layerId )
{
  OutputNALUnit nalu(NAL_UNIT_PPS);
  m_HLSWriter->setBitstream( &nalu.m_Bitstream );
Philippe Bordes's avatar
Philippe Bordes committed
#if RPR_ENABLE
  nalu.m_temporalId = accessUnit.temporalId;
#endif
  CHECK( nalu.m_temporalId < accessUnit.temporalId, "TemporalId shall be greater than or equal to the TemporalId of the layer access unit containing the NAL unit" );
  m_HLSWriter->codePPS( pps );
  accessUnit.push_back(new NALUnitEBSP(nalu));
  return (int)(accessUnit.back()->m_nalUnitData.str().size()) * 8;
}

int EncGOP::xWriteAPS( AccessUnit &accessUnit, APS *aps, const int layerId, const bool isPrefixNUT )
{
  OutputNALUnit nalu( isPrefixNUT ? NAL_UNIT_PREFIX_APS : NAL_UNIT_SUFFIX_APS );
Vadim Seregin's avatar
Vadim Seregin committed
  aps->setLayerId( layerId );
#if EMBEDDED_APS 
  m_aps.push_back( *aps );
  return 0;
#endif
Hendry's avatar
Hendry committed
  m_HLSWriter->setBitstream(&nalu.m_Bitstream);
  nalu.m_nuhLayerId = layerId;
  nalu.m_temporalId = aps->getTemporalId();
  CHECK( nalu.m_temporalId < accessUnit.temporalId, "TemporalId shall be greater than or equal to the TemporalId of the layer access unit containing the NAL unit" );
Vadim Seregin's avatar
Vadim Seregin committed
#if EMBEDDED_APS
  m_HLSWriter->codeAPS( aps, true );
#else
  m_HLSWriter->codeAPS( aps );
#endif
Hendry's avatar
Hendry committed
  accessUnit.push_back(new NALUnitEBSP(nalu));
  return (int)(accessUnit.back()->m_nalUnitData.str().size()) * 8;
}
BDChoi's avatar
BDChoi committed
int EncGOP::xWriteParameterSets(AccessUnit &accessUnit, Slice *slice, const bool bSeqFirst, const int layerIdx)
Vadim Seregin's avatar
Vadim Seregin committed
  if( bSeqFirst )
  {
BDChoi's avatar
BDChoi committed
    if (layerIdx == 0)
    {
Kai Zhang's avatar
Kai Zhang committed
      if (m_pcCfg->getDCIEnabled())
      {
        actualTotalBits += xWriteDCI(accessUnit, m_pcEncLib->getDCI());
      }
BDChoi's avatar
BDChoi committed
      if (slice->getSPS()->getVPSId() != 0)
      {
        actualTotalBits += xWriteVPS(accessUnit, m_pcEncLib->getVPS());
      }
    }
Vadim Seregin's avatar
Vadim Seregin committed
    if( m_pcEncLib->SPSNeedsWriting( slice->getSPS()->getSPSId() ) ) // Note this assumes that all changes to the SPS are made at the EncLib level prior to picture creation (EncLib::xGetNewPicBuffer).
    {
      CHECK( !( bSeqFirst ), "Unspecified error" ); // Implementations that use more than 1 SPS need to be aware of activation issues.
      actualTotalBits += xWriteSPS( accessUnit, slice->getSPS(), m_pcEncLib->getLayerId() );
Vadim Seregin's avatar
Vadim Seregin committed
    }
  }

  if( m_pcEncLib->PPSNeedsWriting( slice->getPPS()->getPPSId() ) ) // Note this assumes that all changes to the PPS are made at the EncLib level prior to picture creation (EncLib::xGetNewPicBuffer).
  {
#if JVET_AC0096
    if (m_pcEncLib->getRprPopulatePPSatIntraFlag())
    {
      if (slice->isIntra())
      {
        actualTotalBits += xWritePPS(accessUnit, slice->getPPS(), m_pcEncLib->getLayerId());
        if (!(slice->getPPS()->getPPSId() == 0))
        {
          const PPS* pPPS = m_pcEncLib->getPPS(0);
          actualTotalBits += xWritePPS(accessUnit, pPPS, m_pcEncLib->getLayerId());
        }
        if (!(slice->getPPS()->getPPSId() == ENC_PPS_ID_RPR))
        {
          const PPS* pPPS = m_pcEncLib->getPPS(ENC_PPS_ID_RPR);
          actualTotalBits += xWritePPS(accessUnit, pPPS, m_pcEncLib->getLayerId());
        }
        if (!(slice->getPPS()->getPPSId() == ENC_PPS_ID_RPR2))
        {
          const PPS* pPPS = m_pcEncLib->getPPS(ENC_PPS_ID_RPR2);
          actualTotalBits += xWritePPS(accessUnit, pPPS, m_pcEncLib->getLayerId());
        }
        if (!(slice->getPPS()->getPPSId() == ENC_PPS_ID_RPR3))
        {
          const PPS* pPPS = m_pcEncLib->getPPS(ENC_PPS_ID_RPR3);
          actualTotalBits += xWritePPS(accessUnit, pPPS, m_pcEncLib->getLayerId());
        }
      }
      else
      {
        if (!(slice->getPPS()->getPPSId() == 0) && !(slice->getPPS()->getPPSId() == ENC_PPS_ID_RPR) && !(slice->getPPS()->getPPSId() == ENC_PPS_ID_RPR2) && !(slice->getPPS()->getPPSId() == ENC_PPS_ID_RPR3))
        {
          const PPS* pPPS = m_pcEncLib->getPPS(0);
          actualTotalBits += xWritePPS(accessUnit, pPPS, m_pcEncLib->getLayerId());
        }
      }
    }
    else
    {
      actualTotalBits += xWritePPS(accessUnit, slice->getPPS(), m_pcEncLib->getLayerId());
    }
#else
    actualTotalBits += xWritePPS( accessUnit, slice->getPPS(), m_pcEncLib->getLayerId() );
Vadim Seregin's avatar
Vadim Seregin committed
  }
Brian Heng's avatar
Brian Heng committed
int EncGOP::xWritePicHeader( AccessUnit &accessUnit, PicHeader *picHeader )
{
  OutputNALUnit nalu(NAL_UNIT_PH);
  m_HLSWriter->setBitstream( &nalu.m_Bitstream );
  nalu.m_temporalId = accessUnit.temporalId;
  nalu.m_nuhLayerId = m_pcEncLib->getLayerId();
Vadim Seregin's avatar
Vadim Seregin committed

#if EMBEDDED_APS
  m_HLSWriter->codePictureHeader( picHeader, true, m_aps );
#else
  m_HLSWriter->codePictureHeader( picHeader, true );
Vadim Seregin's avatar
Vadim Seregin committed
#endif
Brian Heng's avatar
Brian Heng committed
  accessUnit.push_back(new NALUnitEBSP(nalu));
  return (int)(accessUnit.back()->m_nalUnitData.str().size()) * 8;
}

void EncGOP::xWriteAccessUnitDelimiter (AccessUnit &accessUnit, Slice *slice)
{
  AUDWriter audWriter;
  OutputNALUnit nalu(NAL_UNIT_ACCESS_UNIT_DELIMITER);
  int vpsId = slice->getSPS()->getVPSId();
    nalu.m_nuhLayerId = slice->getVPS()->getLayerId(0);
  }
  CHECK( nalu.m_temporalId != accessUnit.temporalId, "TemporalId shall be equal to the TemporalId of the AU containing the NAL unit" );
  int picType = slice->isIntra() ? 0 : (slice->isInterP() ? 1 : 2);
  audWriter.codeAUD(nalu.m_Bitstream, m_audIrapOrGdrAuFlag, picType);
  accessUnit.push_front(new NALUnitEBSP(nalu));
}

// write SEI list into one NAL unit and add it to the Access unit at auPos
void EncGOP::xWriteSEI (NalUnitType naluType, SEIMessages& seiMessages, AccessUnit &accessUnit, AccessUnit::iterator &auPos, int temporalId)
{
  // don't do anything, if we get an empty list
  if (seiMessages.empty())
  {
    return;
  }
  OutputNALUnit nalu( naluType, m_pcEncLib->getLayerId(), temporalId );
  m_seiWriter.writeSEImessages(nalu.m_Bitstream, seiMessages, *m_HRD, false, temporalId);
  auPos = accessUnit.insert(auPos, new NALUnitEBSP(nalu));
  auPos++;
}

void EncGOP::xWriteSEISeparately (NalUnitType naluType, SEIMessages& seiMessages, AccessUnit &accessUnit, AccessUnit::iterator &auPos, int temporalId)
{
  // don't do anything, if we get an empty list
  if (seiMessages.empty())
  {
    return;
  }
  for (SEIMessages::const_iterator sei = seiMessages.begin(); sei!=seiMessages.end(); sei++ )
  {
    SEIMessages tmpMessages;
    tmpMessages.push_back(*sei);
    OutputNALUnit nalu( naluType, m_pcEncLib->getLayerId(), temporalId );
    m_seiWriter.writeSEImessages(nalu.m_Bitstream, tmpMessages, *m_HRD, false, temporalId);
    auPos = accessUnit.insert(auPos, new NALUnitEBSP(nalu));
    auPos++;
  }
}

void EncGOP::xClearSEIs(SEIMessages& seiMessages, bool deleteMessages)
{
  if (deleteMessages)
  {
    deleteSEIs(seiMessages);
  }
  else
  {
    seiMessages.clear();
  }
}

// write SEI messages as separate NAL units ordered
void EncGOP::xWriteLeadingSEIOrdered (SEIMessages& seiMessages, SEIMessages& duInfoSeiMessages, AccessUnit &accessUnit, int temporalId, bool testWrite)
{
  AccessUnit::iterator itNalu = accessUnit.begin();

  while ((itNalu != accessUnit.end()) &&
    ((*itNalu)->m_nalUnitType == NAL_UNIT_ACCESS_UNIT_DELIMITER
      || (*itNalu)->m_nalUnitType == NAL_UNIT_VPS
      || (*itNalu)->m_nalUnitType == NAL_UNIT_DCI
      || (*itNalu)->m_nalUnitType == NAL_UNIT_SPS
      || (*itNalu)->m_nalUnitType == NAL_UNIT_PPS
      ))
  {
    itNalu++;
  }

  SEIMessages localMessages = seiMessages;
  SEIMessages currentMessages;

#if ENABLE_TRACING
  g_HLSTraceEnable = !testWrite;
#endif
  // The case that a specific SEI is not present is handled in xWriteSEI (empty list)

  // Buffering period SEI must always be following active parameter sets
  currentMessages = extractSeisByType(localMessages, SEI::BUFFERING_PERIOD);
  CHECK(!(currentMessages.size() <= 1), "Unspecified error");
  xWriteSEI(NAL_UNIT_PREFIX_SEI, currentMessages, accessUnit, itNalu, temporalId);
  xClearSEIs(currentMessages, !testWrite);

  // Picture timing SEI must always be following buffering period
  currentMessages = extractSeisByType(localMessages, SEI::PICTURE_TIMING);
  CHECK(!(currentMessages.size() <= 1), "Unspecified error");
  xWriteSEI(NAL_UNIT_PREFIX_SEI, currentMessages, accessUnit, itNalu, temporalId);
  xClearSEIs(currentMessages, !testWrite);

  // Decoding unit info SEI must always be following picture timing
  if (!duInfoSeiMessages.empty())
  {
    currentMessages.push_back(duInfoSeiMessages.front());
    if (!testWrite)
    {
      duInfoSeiMessages.pop_front();
    }
    xWriteSEI(NAL_UNIT_PREFIX_SEI, currentMessages, accessUnit, itNalu, temporalId);
  if (m_pcCfg->getScalableNestingSEIEnabled())
  {
    // Scalable nesting SEI must always be the following DU info
    currentMessages = extractSeisByType(localMessages, SEI::SCALABLE_NESTING);
    xWriteSEISeparately(NAL_UNIT_PREFIX_SEI, currentMessages, accessUnit, itNalu, temporalId);
    xClearSEIs(currentMessages, !testWrite);
  }
  xWriteSEISeparately(NAL_UNIT_PREFIX_SEI, localMessages, accessUnit, itNalu, temporalId);
  xClearSEIs(localMessages, !testWrite);

  if (!testWrite)
  {
    seiMessages.clear();
  }
}


void EncGOP::xWriteLeadingSEIMessages (SEIMessages& seiMessages, SEIMessages& duInfoSeiMessages, AccessUnit &accessUnit, int temporalId, const SPS *sps, std::deque<DUData> &duData)
{
  AccessUnit testAU;
  SEIMessages picTimingSEIs = getSeisByType(seiMessages, SEI::PICTURE_TIMING);
  CHECK(!(picTimingSEIs.size() < 2), "Unspecified error");
  SEIPictureTiming * picTiming = picTimingSEIs.empty() ? NULL : (SEIPictureTiming*) picTimingSEIs.front();

  // test writing
  xWriteLeadingSEIOrdered(seiMessages, duInfoSeiMessages, testAU, temporalId, true);
  // update Timing and DU info SEI
  xUpdateDuData(testAU, duData);
  xUpdateTimingSEI(picTiming, duData, sps);
  xUpdateDuInfoSEI(duInfoSeiMessages, picTiming, sps->getMaxTLayers());
  xWriteLeadingSEIOrdered(seiMessages, duInfoSeiMessages, accessUnit, temporalId, false);

  // testAU will automatically be cleaned up when losing scope
}

void EncGOP::xWriteTrailingSEIMessages (SEIMessages& seiMessages, AccessUnit &accessUnit, int temporalId)
{
  // Note: using accessUnit.end() works only as long as this function is called after slice coding and before EOS/EOB NAL units
  AccessUnit::iterator pos = accessUnit.end();
  xWriteSEISeparately(NAL_UNIT_SUFFIX_SEI, seiMessages, accessUnit, pos, temporalId);
void EncGOP::xWriteDuSEIMessages (SEIMessages& duInfoSeiMessages, AccessUnit &accessUnit, int temporalId, std::deque<DUData> &duData)
  if( m_pcCfg->getDecodingUnitInfoSEIEnabled() && m_HRD->getBufferingPeriodSEI()->m_decodingUnitCpbParamsInPicTimingSeiFlag )
  {
    int naluIdx = 0;
    AccessUnit::iterator nalu = accessUnit.begin();

    // skip over first DU, we have a DU info SEI there already
    while (naluIdx < duData[0].accumNalsDU && nalu!=accessUnit.end())
    {
      naluIdx++;
      nalu++;
    }

    SEIMessages::iterator duSEI = duInfoSeiMessages.begin();
    // loop over remaining DUs
    for (int duIdx = 1; duIdx < duData.size(); duIdx++)
    {
      if (duSEI == duInfoSeiMessages.end())
      {
        // if the number of generated SEIs matches the number of DUs, this should not happen
        CHECK(!(false), "Unspecified error");
        return;
      }
      // write the next SEI
      SEIMessages tmpSEI;
      tmpSEI.push_back(*duSEI);
      xWriteSEI(NAL_UNIT_PREFIX_SEI, tmpSEI, accessUnit, nalu, temporalId);
      // nalu points to the position after the SEI, so we have to increase the index as well
      naluIdx++;
      while ((naluIdx < duData[duIdx].accumNalsDU) && nalu!=accessUnit.end())
      {
        naluIdx++;
        nalu++;
      }
      duSEI++;
    }
  }
  deleteSEIs(duInfoSeiMessages);
}


void EncGOP::xCreateIRAPLeadingSEIMessages (SEIMessages& seiMessages, const SPS *sps, const PPS *pps)
{
  OutputNALUnit nalu(NAL_UNIT_PREFIX_SEI);

  if(m_pcCfg->getFramePackingArrangementSEIEnabled())
  {
    SEIFramePacking *sei = new SEIFramePacking;
    m_seiEncoder.initSEIFramePacking (sei, m_iNumPicCoded);
    seiMessages.push_back(sei);
  }

  if (m_pcCfg->getParameterSetsInclusionIndicationSEIEnabled())
  {
    SEIParameterSetsInclusionIndication* sei = new SEIParameterSetsInclusionIndication;
    m_seiEncoder.initSEIParameterSetsInclusionIndication(sei);
    seiMessages.push_back(sei);
  }

#if U0033_ALTERNATIVE_TRANSFER_CHARACTERISTICS_SEI
  if(m_pcCfg->getSEIAlternativeTransferCharacteristicsSEIEnable())
  {
    SEIAlternativeTransferCharacteristics *seiAlternativeTransferCharacteristics = new SEIAlternativeTransferCharacteristics;
    m_seiEncoder.initSEIAlternativeTransferCharacteristics(seiAlternativeTransferCharacteristics);
    seiMessages.push_back(seiAlternativeTransferCharacteristics);
  }
  if (m_pcCfg->getErpSEIEnabled())
  {
    SEIEquirectangularProjection *sei = new SEIEquirectangularProjection;
    m_seiEncoder.initSEIErp(sei);
    seiMessages.push_back(sei);
  }

  if (m_pcCfg->getSphereRotationSEIEnabled())
  {
    SEISphereRotation *sei = new SEISphereRotation;
    m_seiEncoder.initSEISphereRotation(sei);
    seiMessages.push_back(sei);
  }

  if (m_pcCfg->getOmniViewportSEIEnabled())
  {
    SEIOmniViewport *sei = new SEIOmniViewport;
    m_seiEncoder.initSEIOmniViewport(sei);
    seiMessages.push_back(sei);
  }
  if (m_pcCfg->getRwpSEIEnabled())
  {
    SEIRegionWisePacking *seiRegionWisePacking = new SEIRegionWisePacking;
    m_seiEncoder.initSEIRegionWisePacking(seiRegionWisePacking);
    seiMessages.push_back(seiRegionWisePacking);
  }
  if (m_pcCfg->getGcmpSEIEnabled())
  {
    SEIGeneralizedCubemapProjection *sei = new SEIGeneralizedCubemapProjection;
    m_seiEncoder.initSEIGcmp(sei);
    seiMessages.push_back(sei);
  }
  if (m_pcCfg->getSubpicureLevelInfoSEICfg().m_enabled)
  {
    SEISubpicureLevelInfo *seiSubpicureLevelInfo = new SEISubpicureLevelInfo;
    m_seiEncoder.initSEISubpictureLevelInfo(seiSubpicureLevelInfo, sps);
    seiMessages.push_back(seiSubpicureLevelInfo);
  }
  if (m_pcCfg->getSampleAspectRatioInfoSEIEnabled())
  {
    SEISampleAspectRatioInfo *seiSampleAspectRatioInfo = new SEISampleAspectRatioInfo;
    m_seiEncoder.initSEISampleAspectRatioInfo(seiSampleAspectRatioInfo);
    seiMessages.push_back(seiSampleAspectRatioInfo);
  }
Taoran Lu's avatar
Taoran Lu committed
  // film grain
  if (m_pcCfg->getFilmGrainCharactersticsSEIEnabled())
  {
    SEIFilmGrainCharacteristics *sei = new SEIFilmGrainCharacteristics;
    m_seiEncoder.initSEIFilmGrainCharacteristics(sei);
    seiMessages.push_back(sei);
  }

  // mastering display colour volume
  if (m_pcCfg->getMasteringDisplaySEI().colourVolumeSEIEnabled)
  {
    SEIMasteringDisplayColourVolume *sei = new SEIMasteringDisplayColourVolume;
    m_seiEncoder.initSEIMasteringDisplayColourVolume(sei);
    seiMessages.push_back(sei);
  }

  // content light level
  if (m_pcCfg->getCLLSEIEnabled())
  {
    SEIContentLightLevelInfo *seiCLL = new SEIContentLightLevelInfo;
    m_seiEncoder.initSEIContentLightLevel(seiCLL);
    seiMessages.push_back(seiCLL);
  }

Taoran Lu's avatar
Taoran Lu committed
  // ambient viewing environment
Taoran Lu's avatar
Taoran Lu committed
  if (m_pcCfg->getAmbientViewingEnvironmentSEIEnabled())
  {
    SEIAmbientViewingEnvironment *seiAVE = new SEIAmbientViewingEnvironment;
    m_seiEncoder.initSEIAmbientViewingEnvironment(seiAVE);
    seiMessages.push_back(seiAVE);
  }

  // content colour volume
  if (m_pcCfg->getCcvSEIEnabled())
  {
    SEIContentColourVolume *seiContentColourVolume = new SEIContentColourVolume;
    m_seiEncoder.initSEIContentColourVolume(seiContentColourVolume);
    seiMessages.push_back(seiContentColourVolume);
  }
}

void EncGOP::xCreatePerPictureSEIMessages (int picInGOP, SEIMessages& seiMessages, SEIMessages& nestedSeiMessages, Slice *slice)
{
  if ((m_pcCfg->getBufferingPeriodSEIEnabled()) && (slice->isIRAP() || slice->getNalUnitType() == NAL_UNIT_CODED_SLICE_GDR) &&
    slice->getNalUnitLayerId()==slice->getVPS()->getLayerId(0) &&
Xiang Ma's avatar
Xiang Ma committed
  (slice->getSPS()->getGeneralHrdParametersPresentFlag()))
  {
    SEIBufferingPeriod *bufferingPeriodSEI = new SEIBufferingPeriod();
    bool noLeadingPictures = ( (slice->getNalUnitType()!= NAL_UNIT_CODED_SLICE_IDR_W_RADL) && (slice->getNalUnitType()!= NAL_UNIT_CODED_SLICE_CRA) )?(true):(false);
    m_seiEncoder.initSEIBufferingPeriod(bufferingPeriodSEI,noLeadingPictures);
    m_HRD->setBufferingPeriodSEI(bufferingPeriodSEI);
    seiMessages.push_back(bufferingPeriodSEI);
    m_bufferingPeriodSEIPresentInAU = true;

    if (m_pcCfg->getScalableNestingSEIEnabled())
    {
      SEIBufferingPeriod *bufferingPeriodSEIcopy = new SEIBufferingPeriod();
      bufferingPeriodSEI->copyTo(*bufferingPeriodSEIcopy);
      nestedSeiMessages.push_back(bufferingPeriodSEIcopy);
    }
  if (m_pcEncLib->getDependentRAPIndicationSEIEnabled() && slice->isDRAP())
  {
    SEIDependentRAPIndication *dependentRAPIndicationSEI = new SEIDependentRAPIndication();
    m_seiEncoder.initSEIDependentRAPIndication(dependentRAPIndicationSEI);
    seiMessages.push_back(dependentRAPIndicationSEI);
  }

#if JVET_R0294_SUBPIC_HASH
void EncGOP::xCreateScalableNestingSEI(SEIMessages& seiMessages, SEIMessages& nestedSeiMessages, const std::vector<int> &targetOLSs, const std::vector<int> &targetLayers, const std::vector<uint16_t>& subpicIDs)
#else
void EncGOP::xCreateScalableNestingSEI(SEIMessages& seiMessages, SEIMessages& nestedSeiMessages, const std::vector<uint16_t>& subpicIDs)
{
  SEIMessages tmpMessages;
  while (!nestedSeiMessages.empty())
  {
    SEI* sei = nestedSeiMessages.front();
    nestedSeiMessages.pop_front();
    tmpMessages.push_back(sei);
    SEIScalableNesting *nestingSEI = new SEIScalableNesting();
#if JVET_R0294_SUBPIC_HASH
    m_seiEncoder.initSEIScalableNesting(nestingSEI, tmpMessages, targetOLSs, targetLayers, subpicIDs);
#else
    m_seiEncoder.initSEIScalableNesting(nestingSEI, tmpMessages, subpicIDs);
void EncGOP::xCreateFrameFieldInfoSEI  (SEIMessages& seiMessages, Slice *slice, bool isField)
{
  if (m_pcCfg->getFrameFieldInfoSEIEnabled())
  {
    SEIFrameFieldInfo *frameFieldInfoSEI = new SEIFrameFieldInfo();

    // encode only very basic information. if more feature are supported, this should be moved to SEIEncoder
    frameFieldInfoSEI->m_fieldPicFlag = isField;
    if (isField)
    {
      frameFieldInfoSEI->m_bottomFieldFlag = !slice->getPic()->topField;
    }
    seiMessages.push_back(frameFieldInfoSEI);
  }
}


void EncGOP::xCreatePictureTimingSEI  (int IRAPGOPid, SEIMessages& seiMessages, SEIMessages& nestedSeiMessages, SEIMessages& duInfoSeiMessages, Slice *slice, bool isField, std::deque<DUData> &duData)
{
  // Picture timing depends on buffering period. When either of those is not disabled,
  // initialization would fail. Needs more cleanup after DU timing is integrated.
  if (!(m_pcCfg->getPictureTimingSEIEnabled() && m_pcCfg->getBufferingPeriodSEIEnabled()))
  {
    return;
  }

Xiang Ma's avatar
Xiang Ma committed
  const GeneralHrdParams *hrd = slice->getSPS()->getGeneralHrdParameters();
  if ((m_pcCfg->getPictureTimingSEIEnabled() || m_pcCfg->getDecodingUnitInfoSEIEnabled()) && slice->getNalUnitLayerId() == slice->getVPS()->getLayerId(0))
  {
    int picSptDpbOutputDuDelay = 0;
    SEIPictureTiming *pictureTimingSEI = new SEIPictureTiming();

    // DU parameters
    if( hrd->getGeneralDecodingUnitHrdParamsPresentFlag() )
    {
      uint32_t numDU = (uint32_t) duData.size();
      pictureTimingSEI->m_numDecodingUnitsMinus1     = ( numDU - 1 );
      pictureTimingSEI->m_duCommonCpbRemovalDelayFlag = false;
      pictureTimingSEI->m_numNalusInDuMinus1.resize( numDU );
      const uint32_t maxNumSubLayers = slice->getSPS()->getMaxTLayers();
      pictureTimingSEI->m_duCpbRemovalDelayMinus1.resize( numDU * maxNumSubLayers );
    const uint32_t cpbRemovalDelayLegth = m_HRD->getBufferingPeriodSEI()->m_cpbRemovalDelayLength;
    const uint32_t maxNumSubLayers = slice->getSPS()->getMaxTLayers();
    pictureTimingSEI->m_auCpbRemovalDelay[maxNumSubLayers-1] = std::min<int>(std::max<int>(1, m_totalCoded[maxNumSubLayers-1] - m_lastBPSEI[maxNumSubLayers-1]), static_cast<int>(pow(2, static_cast<double>(cpbRemovalDelayLegth)))); // Syntax element signalled as minus, hence the .
    CHECK( (m_totalCoded[maxNumSubLayers-1] - m_lastBPSEI[maxNumSubLayers-1]) > pow(2, static_cast<double>(cpbRemovalDelayLegth)), " cpbRemovalDelayLegth too small for m_auCpbRemovalDelay[pt_max_sub_layers_minus1] at picture timing SEI " );
    const uint32_t temporalId = slice->getTLayer();
    for( int i = temporalId ; i < maxNumSubLayers - 1 ; i ++ )
    {
      int indexWithinGOP = (m_totalCoded[maxNumSubLayers - 1] - m_lastBPSEI[maxNumSubLayers - 1]) % m_pcCfg->getGOPSize();
      pictureTimingSEI->m_ptSubLayerDelaysPresentFlag[i] = true;
Virginie Drugeon's avatar
Virginie Drugeon committed
      if( ((m_rapWithLeading == true) && (indexWithinGOP == 0)) || (m_totalCoded[maxNumSubLayers - 1] == 0) || m_bufferingPeriodSEIPresentInAU || (slice->getPOC() + m_pcCfg->getGOPSize()) > m_pcCfg->getFramesToBeEncoded() )
      {
        pictureTimingSEI->m_cpbRemovalDelayDeltaEnabledFlag[i] = false;
      }
      else
      {
        pictureTimingSEI->m_cpbRemovalDelayDeltaEnabledFlag[i] = m_HRD->getBufferingPeriodSEI()->m_cpbRemovalDelayDeltasPresentFlag;
      }
      if( pictureTimingSEI->m_cpbRemovalDelayDeltaEnabledFlag[i] )
      {
        if( m_rapWithLeading == false )
        {
          switch (m_pcCfg->getGOPSize())
          {
            case 8:
            {
              if((indexWithinGOP == 1 && i == 2))
              {
                pictureTimingSEI->m_cpbRemovalDelayDeltaIdx[i] = 0;
              }
              else if((indexWithinGOP == 2 && i == 2) || (indexWithinGOP == 6 && i == 2))
              {
                pictureTimingSEI->m_cpbRemovalDelayDeltaIdx[i] = 1;
              }
              else if((indexWithinGOP == 1 && i == 1) || (indexWithinGOP == 3 && i == 2))
              {
                pictureTimingSEI->m_cpbRemovalDelayDeltaIdx[i] = 2;
              }
              else if(indexWithinGOP == 2 && i == 1)
              {
                pictureTimingSEI->m_cpbRemovalDelayDeltaIdx[i] = 3;
              }
              else if(indexWithinGOP == 1 && i == 0)
              {
                pictureTimingSEI->m_cpbRemovalDelayDeltaIdx[i] = 4;
              }
              else
              {
                THROW("m_cpbRemovalDelayDeltaIdx not applicable for the sub-layer and GOP size");
              }
            }
              break;
            case 16:
            {
              if((indexWithinGOP == 1 && i == 3))
              {
                pictureTimingSEI->m_cpbRemovalDelayDeltaIdx[i] = 0;
              }
              else if((indexWithinGOP == 2 && i == 3) || (indexWithinGOP == 10 && i == 3) || (indexWithinGOP == 14 && i == 3))
              {
                pictureTimingSEI->m_cpbRemovalDelayDeltaIdx[i] = 1;
              }
              else if((indexWithinGOP == 1 && i == 2) || (indexWithinGOP == 3 && i == 3) || (indexWithinGOP == 7 && i == 3) || (indexWithinGOP == 11 && i == 3))
              {
                pictureTimingSEI->m_cpbRemovalDelayDeltaIdx[i] = 2;
              }
              else if(indexWithinGOP == 4 && i == 3)
              {
                pictureTimingSEI->m_cpbRemovalDelayDeltaIdx[i] = 3;
              }
              else if((indexWithinGOP == 2 && i == 2) || (indexWithinGOP == 10 && i == 2))
              {
                pictureTimingSEI->m_cpbRemovalDelayDeltaIdx[i] = 4;
              }
              else if(indexWithinGOP == 1 && i == 1)
              {
                pictureTimingSEI->m_cpbRemovalDelayDeltaIdx[i] = 5;
              }
              else if(indexWithinGOP == 3 && i == 2)
              {
                pictureTimingSEI->m_cpbRemovalDelayDeltaIdx[i] = 6;
              }
              else if(indexWithinGOP == 2 && i == 1)
              {
                pictureTimingSEI->m_cpbRemovalDelayDeltaIdx[i] = 7;
              }
              else if(indexWithinGOP == 1 && i == 0)
              {
                pictureTimingSEI->m_cpbRemovalDelayDeltaIdx[i] = 8;
              }
              else
              {
                THROW("m_cpbRemovalDelayDeltaIdx not applicable for the sub-layer and GOP size");
              THROW("m_cpbRemovalDelayDeltaIdx not supported for the current GOP size");
            }
              break;
          }
        }
        else
        {
          switch (m_pcCfg->getGOPSize())
          {
            case 8:
            {
              if((indexWithinGOP == 1 && i == 2) || (indexWithinGOP == 5 && i == 2))
              {
                pictureTimingSEI->m_cpbRemovalDelayDeltaIdx[i] = 0;
              }
              else if(indexWithinGOP == 2 && i == 2)
              {
                pictureTimingSEI->m_cpbRemovalDelayDeltaIdx[i] = 1;
              }
              else if(indexWithinGOP == 1 && i == 1)
              {
                pictureTimingSEI->m_cpbRemovalDelayDeltaIdx[i] = 2;
              }
              else
              {
                THROW("m_cpbRemovalDelayDeltaIdx not applicable for the sub-layer and GOP size");
              }
            }
              break;
            case 16:
            {
              if((indexWithinGOP == 1 && i == 3) || (indexWithinGOP == 9 && i == 3) || (indexWithinGOP == 13 && i == 3))
              {
                pictureTimingSEI->m_cpbRemovalDelayDeltaIdx[i] = 0;
              }
              else if((indexWithinGOP == 2 && i == 3) || (indexWithinGOP == 6 && i == 3) || (indexWithinGOP == 10 && i == 3))
              {
                pictureTimingSEI->m_cpbRemovalDelayDeltaIdx[i] = 1;
              }
              else if((indexWithinGOP == 1 && i == 2) || (indexWithinGOP == 9 && i == 2) || (indexWithinGOP == 3 && i == 3))
              {
                pictureTimingSEI->m_cpbRemovalDelayDeltaIdx[i] = 2;
              }
              else if(indexWithinGOP == 2 && i == 2)
              {
                pictureTimingSEI->m_cpbRemovalDelayDeltaIdx[i] = 3;
              }
              else if(indexWithinGOP == 1 && i == 1)
              {
                pictureTimingSEI->m_cpbRemovalDelayDeltaIdx[i] = 4;
              }
              else
              {
                THROW("m_cpbRemovalDelayDeltaIdx not applicable for the sub-layer and GOP size");
              THROW("m_cpbRemovalDelayDeltaIdx not applicable for the sub-layer and GOP size");
        int scaledDistToBuffPeriod = (m_totalCoded[i] - m_lastBPSEI[i]) * static_cast<int>(pow(2, static_cast<double>(maxNumSubLayers - 1 - i)));
        pictureTimingSEI->m_auCpbRemovalDelay[i] = std::min<int>(std::max<int>(1, scaledDistToBuffPeriod), static_cast<int>(pow(2, static_cast<double>(cpbRemovalDelayLegth)))); // Syntax element signalled as minus, hence the .
        CHECK( (scaledDistToBuffPeriod) > pow(2, static_cast<double>(cpbRemovalDelayLegth)), " cpbRemovalDelayLegth too small for m_auCpbRemovalDelay[i] at picture timing SEI " );
      }
    }
    pictureTimingSEI->m_picDpbOutputDelay = slice->getSPS()->getNumReorderPics(slice->getSPS()->getMaxTLayers()-1) + slice->getPOC() - m_totalCoded[maxNumSubLayers-1];
    if(m_pcCfg->getEfficientFieldIRAPEnabled() && IRAPGOPid > 0 && IRAPGOPid < m_iGopSize)
    {
      // if pictures have been swapped there is likely one more picture delay on their tid. Very rough approximation
      pictureTimingSEI->m_picDpbOutputDelay ++;
    }
    int factor = hrd->getTickDivisorMinus2() + 2;
    pictureTimingSEI->m_picDpbOutputDuDelay = factor * pictureTimingSEI->m_picDpbOutputDelay;
    if( m_pcCfg->getDecodingUnitInfoSEIEnabled() )
    {
      picSptDpbOutputDuDelay = factor * pictureTimingSEI->m_picDpbOutputDelay;
    }
    if (m_bufferingPeriodSEIPresentInAU)
    {
      for( int i = temporalId ; i < maxNumSubLayers ; i ++ )
      {
        m_lastBPSEI[i] = m_totalCoded[i];
      }
      if( (slice->getNalUnitType() == NAL_UNIT_CODED_SLICE_IDR_W_RADL)||(slice->getNalUnitType() == NAL_UNIT_CODED_SLICE_CRA) )
      {
        m_rapWithLeading = true;
      }
    }


    if( m_pcCfg->getPictureTimingSEIEnabled() )
    {
      seiMessages.push_back(pictureTimingSEI);

      if (m_pcCfg->getScalableNestingSEIEnabled() && !m_pcCfg->getSamePicTimingInAllOLS())
      {
        SEIPictureTiming *pictureTimingSEIcopy = new SEIPictureTiming();
        pictureTimingSEI->copyTo(*pictureTimingSEIcopy);
        nestedSeiMessages.push_back(pictureTimingSEIcopy);
      }
    }

    if( m_pcCfg->getDecodingUnitInfoSEIEnabled() && hrd->getGeneralDecodingUnitHrdParamsPresentFlag() )
    {
      for( int i = 0; i < ( pictureTimingSEI->m_numDecodingUnitsMinus1 + 1 ); i ++ )
      {
        SEIDecodingUnitInfo *duInfoSEI = new SEIDecodingUnitInfo();
        duInfoSEI->m_decodingUnitIdx = i;
        for( int j = temporalId; j <= maxNumSubLayers; j++ )
          duInfoSEI->m_duSptCpbRemovalDelayIncrement[j] = pictureTimingSEI->m_duCpbRemovalDelayMinus1[i*maxNumSubLayers+j] + 1;
        duInfoSEI->m_dpbOutputDuDelayPresentFlag = false;
        duInfoSEI->m_picSptDpbOutputDuDelay = picSptDpbOutputDuDelay;

        duInfoSeiMessages.push_back(duInfoSEI);
      }
    }

    if( !m_pcCfg->getPictureTimingSEIEnabled() && pictureTimingSEI )
    {
      delete pictureTimingSEI;
    }
  }
}

void EncGOP::xUpdateDuData(AccessUnit &testAU, std::deque<DUData> &duData)
{
  if (duData.empty())
  {
    return;
  }
  // fix first
  uint32_t numNalUnits = (uint32_t)testAU.size();
  uint32_t numRBSPBytes = 0;
  for (AccessUnit::const_iterator it = testAU.begin(); it != testAU.end(); it++)
  {
    numRBSPBytes += uint32_t((*it)->m_nalUnitData.str().size());
  }
  duData[0].accumBitsDU += ( numRBSPBytes << 3 );
  duData[0].accumNalsDU += numNalUnits;

  // adapt cumulative sums for all following DUs
  // and add one DU info SEI, if enabled
  for (int i=1; i<duData.size(); i++)
  {
    if (m_pcCfg->getDecodingUnitInfoSEIEnabled())
    {
      numNalUnits  += 1;
      numRBSPBytes += ( 5 << 3 );
    }
    duData[i].accumBitsDU += numRBSPBytes; // probably around 5 bytes
    duData[i].accumNalsDU += numNalUnits;
  }

  // The last DU may have a trailing SEI
  if (m_pcCfg->getDecodedPictureHashSEIType()!=HASHTYPE_NONE)
  {
    duData.back().accumBitsDU += ( 20 << 3 ); // probably around 20 bytes - should be further adjusted, e.g. by type
    duData.back().accumNalsDU += 1;
  }

}
void EncGOP::xUpdateTimingSEI(SEIPictureTiming *pictureTimingSEI, std::deque<DUData> &duData, const SPS *sps)
{
  if (!pictureTimingSEI)
  {
    return;
  }
Xiang Ma's avatar
Xiang Ma committed
  const GeneralHrdParams *hrd = sps->getGeneralHrdParameters();
  if( hrd->getGeneralDecodingUnitHrdParamsPresentFlag() )
  {
    int i;
    uint64_t ui64Tmp;
    uint32_t uiPrev = 0;
    uint32_t numDU = ( pictureTimingSEI->m_numDecodingUnitsMinus1 + 1 );
    std::vector<uint32_t> &rDuCpbRemovalDelayMinus1 = pictureTimingSEI->m_duCpbRemovalDelayMinus1;
    uint32_t maxDiff = ( hrd->getTickDivisorMinus2() + 2 ) - 1;

    int maxNumSubLayers = sps->getMaxTLayers();
    for( int j = 0; j < maxNumSubLayers - 1; j++ )
      pictureTimingSEI->m_ptSubLayerDelaysPresentFlag[j] = false;

    for( i = 0; i < numDU; i ++ )
    {
      pictureTimingSEI->m_numNalusInDuMinus1[ i ]       = ( i == 0 ) ? ( duData[i].accumNalsDU - 1 ) : ( duData[i].accumNalsDU- duData[i-1].accumNalsDU - 1 );
    }

    if( numDU == 1 )
    {
      rDuCpbRemovalDelayMinus1[ 0 + maxNumSubLayers - 1 ] = 0; /* don't care */
      rDuCpbRemovalDelayMinus1[ (numDU - 1) * maxNumSubLayers + maxNumSubLayers - 1 ] = 0;/* by definition */
      uint32_t tmp = 0;
      uint32_t accum = 0;

      for( i = ( numDU - 2 ); i >= 0; i -- )
      {
Xiang Ma's avatar
Xiang Ma committed
        ui64Tmp = (((duData[numDU - 1].accumBitsDU - duData[i].accumBitsDU) * (sps->getGeneralHrdParameters()->getTimeScale() / sps->getGeneralHrdParameters()->getNumUnitsInTick()) * (hrd->getTickDivisorMinus2() + 2)) / (m_pcCfg->getTargetBitrate()));
        if( (uint32_t)ui64Tmp > maxDiff )
        {
          tmp ++;
        }
      }
      uiPrev = 0;

      uint32_t flag = 0;
      for( i = ( numDU - 2 ); i >= 0; i -- )
      {
        flag = 0;
Xiang Ma's avatar
Xiang Ma committed
        ui64Tmp = (((duData[numDU - 1].accumBitsDU - duData[i].accumBitsDU) * (sps->getGeneralHrdParameters()->getTimeScale() / sps->getGeneralHrdParameters()->getNumUnitsInTick()) * (hrd->getTickDivisorMinus2() + 2)) / (m_pcCfg->getTargetBitrate()));

        if( (uint32_t)ui64Tmp > maxDiff )
        {
          if(uiPrev >= maxDiff - tmp)
          {
            ui64Tmp = uiPrev + 1;
            flag = 1;
          }
          else                            ui64Tmp = maxDiff - tmp + 1;
        }
        rDuCpbRemovalDelayMinus1[ i * maxNumSubLayers + maxNumSubLayers - 1 ] = (uint32_t)ui64Tmp - uiPrev - 1;
        if( (int)rDuCpbRemovalDelayMinus1[ i * maxNumSubLayers + maxNumSubLayers - 1 ] < 0 )
        {
          rDuCpbRemovalDelayMinus1[ i * maxNumSubLayers + maxNumSubLayers - 1 ] = 0;
        }
        else if (tmp > 0 && flag == 1)
        {
          tmp --;
        }
        accum += rDuCpbRemovalDelayMinus1[ i * maxNumSubLayers + maxNumSubLayers - 1 ] + 1;
void EncGOP::xUpdateDuInfoSEI(SEIMessages &duInfoSeiMessages, SEIPictureTiming *pictureTimingSEI, int maxSubLayers)
{
  if (duInfoSeiMessages.empty() || (pictureTimingSEI == NULL))
  {
    return;
  }

  int i=0;

  for (SEIMessages::iterator du = duInfoSeiMessages.begin(); du!= duInfoSeiMessages.end(); du++)
  {
    SEIDecodingUnitInfo *duInfoSEI = (SEIDecodingUnitInfo*) (*du);
    duInfoSEI->m_decodingUnitIdx = i;
    for ( int j = 0; j < maxSubLayers; j++ )
    {
      duInfoSEI->m_duiSubLayerDelaysPresentFlag[j] = pictureTimingSEI->m_ptSubLayerDelaysPresentFlag[j];
      duInfoSEI->m_duSptCpbRemovalDelayIncrement[j] = pictureTimingSEI->m_duCpbRemovalDelayMinus1[i*maxSubLayers+j] + 1;
    }
static void
validateMinCrRequirements(const ProfileLevelTierFeatures &plt, std::size_t numBytesInVclNalUnits, const Picture *pPic, const EncCfg *pCfg)
{
  //  numBytesInVclNalUnits shall be less than or equal to
  //     FormatCapabilityFactor * MaxLumaSr * framePeriod / MinCr,
  //     ( = FormatCapabilityFactor * MaxLumaSr / (MinCr * frameRate),
  if (plt.getLevelTierFeatures() && plt.getProfileFeatures() && plt.getLevelTierFeatures()->level!=Level::LEVEL15_5)
  {
    const uint32_t formatCapabilityFactorx1000 = plt.getProfileFeatures()->formatCapabilityFactorx1000;
    const uint64_t maxLumaSr = plt.getLevelTierFeatures()->maxLumaSr;
    const uint32_t frameRate = pCfg->getFrameRate();
    const double   minCr = plt.getMinCr();
    const double   denominator = (minCr * frameRate * 1000);
    if (denominator!=0)
    {
      const double   threshold =(formatCapabilityFactorx1000 * maxLumaSr) / (denominator);

      if (numBytesInVclNalUnits > threshold)
      {
        msg( WARNING, "WARNING: Encoded stream does not meet MinCr requirements numBytesInVclNalUnits (%.0f) must be <= %.0f. Try increasing Qp, tier or level\n",
                      (double) numBytesInVclNalUnits, threshold );
      }
    }
  }
}
#if JVET_Q0406_CABAC_ZERO
static std::size_t
#else
cabac_zero_word_padding(const Slice *const pcSlice,
                        const Picture *const pcPic,
                        const std::size_t binCountsInNalUnits,
                        const std::size_t numBytesInVclNalUnits,
#if JVET_Q0406_CABAC_ZERO
                        const std::size_t numZeroWordsAlreadyInserted,
#endif
                              std::ostringstream &nalUnitData,
                        const bool cabacZeroWordPaddingEnabled,
                        const ProfileLevelTierFeatures &plt)
{
  const SPS &sps=*(pcSlice->getSPS());
  const ChromaFormat format = sps.getChromaFormatIdc();
  const int log2subWidthCxsubHeightC = (::getComponentScaleX(COMPONENT_Cb, format)+::getComponentScaleY(COMPONENT_Cb, format));
  const int minCuWidth  = 1 << pcSlice->getSPS()->getLog2MinCodingBlockSize();
  const int minCuHeight = 1 << pcSlice->getSPS()->getLog2MinCodingBlockSize();
  const int paddedWidth = ( ( pcSlice->getPPS()->getPicWidthInLumaSamples() + minCuWidth - 1 ) / minCuWidth ) * minCuWidth;
  const int paddedHeight = ( ( pcSlice->getPPS()->getPicHeightInLumaSamples() + minCuHeight - 1 ) / minCuHeight ) * minCuHeight;
  const int rawBits = paddedWidth * paddedHeight *
                         (sps.getBitDepth(CHANNEL_TYPE_LUMA) + ((2*sps.getBitDepth(CHANNEL_TYPE_CHROMA))>>log2subWidthCxsubHeightC));
  const int vclByteScaleFactor_x3 = ( 32 + 4 * (plt.getTier()==Level::HIGH ? 1 : 0) );
  const std::size_t threshold = (vclByteScaleFactor_x3*numBytesInVclNalUnits/3) + (rawBits/32);
  // "The value of BinCountsInPicNalUnits shall be less than or equal to vclByteScaleFactor * NumBytesInPicVclNalUnits     + ( RawMinCuBits * PicSizeInMinCbsY ) / 32."
  //               binCountsInNalUnits                  <=               vclByteScaleFactor_x3 * numBytesInVclNalUnits / 3 +   rawBits / 32.
  // If it is currently not, then add cabac_zero_words to increase numBytesInVclNalUnits.
  if (binCountsInNalUnits >= threshold)
  {
    // need to add additional cabac zero words (each one accounts for 3 bytes (=00 00 03)) to increase numBytesInVclNalUnits
    const std::size_t targetNumBytesInVclNalUnits = ((binCountsInNalUnits - (rawBits/32))*3+vclByteScaleFactor_x3-1)/vclByteScaleFactor_x3;

    if (targetNumBytesInVclNalUnits>numBytesInVclNalUnits) // It should be!
    {
#if JVET_Q0406_CABAC_ZERO
      const std::size_t numberOfAdditionalBytesNeeded= std::max<std::size_t>(0, targetNumBytesInVclNalUnits - numBytesInVclNalUnits - numZeroWordsAlreadyInserted * 3);
#else
      const std::size_t numberOfAdditionalBytesNeeded=targetNumBytesInVclNalUnits - numBytesInVclNalUnits;
      const std::size_t numberOfAdditionalCabacZeroWords=(numberOfAdditionalBytesNeeded+2)/3;
      const std::size_t numberOfAdditionalCabacZeroBytes=numberOfAdditionalCabacZeroWords*3;
      if (cabacZeroWordPaddingEnabled)
      {
        std::vector<uint8_t> zeroBytesPadding(numberOfAdditionalCabacZeroBytes, uint8_t(0));
        for(std::size_t i=0; i<numberOfAdditionalCabacZeroWords; i++)
        {
          zeroBytesPadding[i*3+2]=3;  // 00 00 03
        }
        nalUnitData.write(reinterpret_cast<const char*>(&(zeroBytesPadding[0])), numberOfAdditionalCabacZeroBytes);
        msg( NOTICE, "Adding %d bytes of padding\n", uint32_t( numberOfAdditionalCabacZeroWords * 3 ) );
      }
      else
      {
        msg( NOTICE, "Standard would normally require adding %d bytes of padding\n", uint32_t( numberOfAdditionalCabacZeroWords * 3 ) );
      }
#if JVET_Q0406_CABAC_ZERO
      return numberOfAdditionalCabacZeroWords;
#endif
#if JVET_Q0406_CABAC_ZERO
      return 0;
#endif
}

class EfficientFieldIRAPMapping
{
  private:
    int  IRAPGOPid;
    bool IRAPtoReorder;
    bool swapIRAPForward;

  public:
    EfficientFieldIRAPMapping() :
      IRAPGOPid(-1),
      IRAPtoReorder(false),
      swapIRAPForward(false)
    { }

    void initialize(const bool isField, const int gopSize, const int POCLast, const int numPicRcvd, const int lastIDR, EncGOP *pEncGop, EncCfg *pCfg);

    int adjustGOPid(const int gopID);
    int restoreGOPid(const int gopID);
    int GetIRAPGOPid() const { return IRAPGOPid; }
};

void EfficientFieldIRAPMapping::initialize(const bool isField, const int gopSize, const int POCLast, const int numPicRcvd, const int lastIDR, EncGOP *pEncGop, EncCfg *pCfg )
{
  if(isField)
  {
    int pocCurr;
    for ( int iGOPid=0; iGOPid < gopSize; iGOPid++ )
    {
      // determine actual POC
      if(POCLast == 0) //case first frame or first top field
      {
        pocCurr=0;
      }
      else if(POCLast == 1 && isField) //case first bottom field, just like the first frame, the poc computation is not right anymore, we set the right value
      {
        pocCurr = 1;
      }
      else
      {
        pocCurr = POCLast - numPicRcvd + pCfg->getGOPEntry(iGOPid).m_POC - isField;
      }

      // check if POC corresponds to IRAP
      NalUnitType tmpUnitType = pEncGop->getNalUnitType(pocCurr, lastIDR, isField);
      if (tmpUnitType >= NAL_UNIT_CODED_SLICE_IDR_W_RADL && tmpUnitType <= NAL_UNIT_CODED_SLICE_CRA) // if picture is an IRAP
      {
        if(pocCurr%2 == 0 && iGOPid < gopSize-1 && pCfg->getGOPEntry(iGOPid).m_POC == pCfg->getGOPEntry(iGOPid+1).m_POC-1)
        { // if top field and following picture in enc order is associated bottom field
          IRAPGOPid = iGOPid;
          IRAPtoReorder = true;
          swapIRAPForward = true;
          break;
        }
        if(pocCurr%2 != 0 && iGOPid > 0 && pCfg->getGOPEntry(iGOPid).m_POC == pCfg->getGOPEntry(iGOPid-1).m_POC+1)
        {
          // if picture is an IRAP remember to process it first
          IRAPGOPid = iGOPid;
          IRAPtoReorder = true;
          swapIRAPForward = false;
          break;
        }
      }
    }
  }
}

int EfficientFieldIRAPMapping::adjustGOPid(const int GOPid)
{
  if(IRAPtoReorder)
  {
    if(swapIRAPForward)
    {
      if(GOPid == IRAPGOPid)
      {
        return IRAPGOPid +1;
      }
      else if(GOPid == IRAPGOPid +1)
      {
        return IRAPGOPid;
      }
    }
    else
    {
      if(GOPid == IRAPGOPid -1)
      {
        return IRAPGOPid;
      }
      else if(GOPid == IRAPGOPid)
      {
        return IRAPGOPid -1;
      }
    }
  }
  return GOPid;
}

int EfficientFieldIRAPMapping::restoreGOPid(const int GOPid)
{
  if(IRAPtoReorder)
  {
    if(swapIRAPForward)
    {
      if(GOPid == IRAPGOPid)
      {
        IRAPtoReorder = false;
        return IRAPGOPid +1;
      }
      else if(GOPid == IRAPGOPid +1)
      {
        return GOPid -1;
      }
    }
    else
    {
      if(GOPid == IRAPGOPid)
      {
        return IRAPGOPid -1;
      }
      else if(GOPid == IRAPGOPid -1)
      {
        IRAPtoReorder = false;
        return IRAPGOPid;
      }
    }
  }
  return GOPid;
}


static void
printHash(const HashType hashType, const std::string &digestStr)
{
  const char *decodedPictureHashModeName;
  switch (hashType)
  {
    case HASHTYPE_MD5:
      decodedPictureHashModeName = "MD5";
      break;
    case HASHTYPE_CRC:
      decodedPictureHashModeName = "CRC";
      break;
    case HASHTYPE_CHECKSUM:
      decodedPictureHashModeName = "Checksum";
      break;
    default:
      decodedPictureHashModeName = NULL;
      break;
  }
  if (decodedPictureHashModeName != NULL)
  {
    if (digestStr.empty())
    {
      msg( NOTICE, " [%s:%s]", decodedPictureHashModeName, "?");
    }
    else
    {
      msg( NOTICE, " [%s:%s]", decodedPictureHashModeName, digestStr.c_str());
    }
  }
}

bool isPicEncoded( int targetPoc, int curPoc, int curTLayer, int gopSize, int intraPeriod )
{
  int  tarGop = targetPoc / gopSize;
  int  curGop = curPoc / gopSize;

  if( tarGop + 1 == curGop )
  {
    // part of next GOP only for tl0 pics
    return curTLayer == 0;
  }

  int  tarIFr = ( targetPoc / intraPeriod ) * intraPeriod;
  int  curIFr = ( curPoc / intraPeriod ) * intraPeriod;

  if( curIFr != tarIFr )
  {
    return false;
  }

  int  tarId = targetPoc - tarGop * gopSize;

  if( tarGop > curGop )
  {
    return ( tarId == 0 ) ? ( 0 == curTLayer ) : ( 1 >= curTLayer );
  }

  if( tarGop + 1 < curGop )
  {
    return false;
  }

  int  curId = curPoc - curGop * gopSize;
  int  tarTL = 0;

  while( tarId != 0 )
  {
    gopSize /= 2;
    if( tarId >= gopSize )
    {
      tarId -= gopSize;
      if( curId != 0 ) curId -= gopSize;
    }
    else if( curId == gopSize )
    {
      curId = 0;
    }
    tarTL++;
  }

  return curTLayer <= tarTL && curId == 0;
}

void trySkipOrDecodePicture( bool& decPic, bool& encPic, const EncCfg& cfg, Picture* pcPic, ParameterSetMap<APS> *apsMap )
{
  // check if we should decode a leading bitstream
  if( !cfg.getDecodeBitstream( 0 ).empty() )
  {
    static bool bDecode1stPart = true; /* TODO: MT */
    if( bDecode1stPart )
    {
      if( cfg.getForceDecodeBitstream1() )
      {
        if( ( bDecode1stPart = tryDecodePicture( pcPic, pcPic->getPOC(), cfg.getDecodeBitstream( 0 ), apsMap, false ) ) )
Tobias Hinz's avatar
Tobias Hinz committed
        bool dbgCTU = cfg.getDebugCTU() != -1 && cfg.getSwitchPOC() == pcPic->getPOC();

        if( ( bDecode1stPart = ( cfg.getSwitchPOC() != pcPic->getPOC() ) || dbgCTU ) && ( bDecode1stPart = tryDecodePicture( pcPic, pcPic->getPOC(), cfg.getDecodeBitstream( 0 ), apsMap, false, cfg.getDebugCTU(), cfg.getSwitchPOC() ) ) )
Tobias Hinz's avatar
Tobias Hinz committed
        {
          if( dbgCTU )
          {
            encPic = true;
            decPic = false;
            bDecode1stPart = false;

            return;
          }
          decPic = bDecode1stPart;
          return;
        }
        else if( pcPic->getPOC() )
        {
          // reset decoder if used and not required any further
          tryDecodePicture( NULL, 0, std::string( "" ) );
        }
      }
    }

    encPic |= cfg.getForceDecodeBitstream1() && !decPic;
    if( cfg.getForceDecodeBitstream1() ) { return; }
  }


  // check if we should decode a trailing bitstream
  if( ! cfg.getDecodeBitstream(1).empty() )
  {
    const int  iNextKeyPOC    = (1+cfg.getSwitchPOC()  / cfg.getGOPSize())     *cfg.getGOPSize();
    const int  iNextIntraPOC  = (1+(cfg.getSwitchPOC() / cfg.getIntraPeriod()))*cfg.getIntraPeriod();
    const int  iRestartIntraPOC   = iNextIntraPOC + (((iNextKeyPOC == iNextIntraPOC) && cfg.getSwitchDQP() ) ? cfg.getIntraPeriod() : 0);

    bool bDecode2ndPart = (pcPic->getPOC() >= iRestartIntraPOC);
    int expectedPoc = pcPic->getPOC();
    Slice slice0;
    if ( cfg.getBs2ModPOCAndType() )
    {
      expectedPoc = pcPic->getPOC() - iRestartIntraPOC;
      slice0.copySliceInfo( pcPic->slices[ 0 ], false );
    }
    if( bDecode2ndPart && (bDecode2ndPart = tryDecodePicture( pcPic, expectedPoc, cfg.getDecodeBitstream(1), apsMap, true )) )
    {
      decPic = bDecode2ndPart;
      if ( cfg.getBs2ModPOCAndType() )
      {
        for( int i = 0; i < pcPic->slices.size(); i++ )
        {
          pcPic->slices[ i ]->setPOC              ( slice0.getPOC()            );
          if ( pcPic->slices[ i ]->getNalUnitType() != slice0.getNalUnitType()
              && pcPic->slices[ i ]->getIdrPicFlag()
              && slice0.getRapPicFlag()
              && slice0.isIntra() )
          {
            // patch IDR-slice to CRA-Intra-slice
            pcPic->slices[ i ]->setNalUnitType    ( slice0.getNalUnitType()    );
            pcPic->slices[ i ]->setLastIDR        ( slice0.getLastIDR()        );
Brian Heng's avatar
Brian Heng committed
            if ( pcPic->cs->picHeader->getEnableTMVPFlag() )
            {
              pcPic->slices[ i ]->setColFromL0Flag( slice0.getColFromL0Flag()  );
              pcPic->slices[ i ]->setColRefIdx    ( slice0.getColRefIdx()      );
            }
          }
        }
      }
      return;
    }
  }

  // leave here if we do not use forward to poc
  if( ! cfg.useFastForwardToPOC() )
  {
    // let's encode
    encPic   = true;
    return;
  }

  // this is the forward to poc section
  static bool bHitFastForwardPOC = false; /* TODO: MT */
  if( bHitFastForwardPOC || isPicEncoded( cfg.getFastForwardToPOC(), pcPic->getPOC(), pcPic->temporalId, cfg.getGOPSize(), cfg.getIntraPeriod() ) )
  {
    bHitFastForwardPOC |= cfg.getFastForwardToPOC() == pcPic->getPOC(); // once we hit the poc we continue encoding

    if( bHitFastForwardPOC && cfg.getStopAfterFFtoPOC() && cfg.getFastForwardToPOC() != pcPic->getPOC() )
    {
      return;
    }

    //except if FastForwardtoPOC is meant to be a SwitchPOC in thist case drop all preceding pictures
    if( bHitFastForwardPOC && ( cfg.getSwitchPOC() == cfg.getFastForwardToPOC() ) && ( cfg.getFastForwardToPOC() > pcPic->getPOC() ) )
    {
      return;
    }
    // let's encode
    encPic   = true;
  }
}

void EncGOP::xPicInitHashME( Picture *pic, const PPS *pps, PicList &rcListPic )
{
  if (! m_pcCfg->getUseHashME())
  {
    return;
  }

  PicList::iterator iterPic = rcListPic.begin();
  while (iterPic != rcListPic.end())
  {
    Picture* refPic = *(iterPic++);
Vadim Seregin's avatar
Vadim Seregin committed

    if (refPic->poc != pic->poc && refPic->referenced)
    {
      if (!refPic->getHashMap()->isInitial())
      {
        if (refPic->getPOC() == 0)
        {
          Pel* picSrc = refPic->getOrigBuf().get(COMPONENT_Y).buf;
          int stridePic = refPic->getOrigBuf().get(COMPONENT_Y).stride;
          int picWidth = pps->getPicWidthInLumaSamples();
          int picHeight = pps->getPicHeightInLumaSamples();
          int blockSize = 4;
          int allNum = 0;
          int simpleNum = 0;
          for (int j = 0; j <= picHeight - blockSize; j += blockSize)
          {
            for (int i = 0; i <= picWidth - blockSize; i += blockSize)
            {
              Pel* curBlock = picSrc + j * stridePic + i;
              bool isHorSame = true;
              for (int m = 0; m < blockSize&&isHorSame; m++)
              {
                for (int n = 1; n < blockSize&&isHorSame; n++)
                {
                  if (curBlock[m*stridePic] != curBlock[m*stridePic + n])
                  {
                    isHorSame = false;
                  }
                }
              }
              bool isVerSame = true;
              for (int m = 1; m < blockSize&&isVerSame; m++)
              {
                for (int n = 0; n < blockSize&&isVerSame; n++)
                {
                  if (curBlock[n] != curBlock[m*stridePic + n])
                  {
                    isVerSame = false;
                  }
                }
              }
              allNum++;
              if (isHorSame || isVerSame)
              {
                simpleNum++;
              }
            }
          }
Vadim Seregin's avatar
Vadim Seregin committed

          if (simpleNum < 0.3*allNum)
          {
            m_pcCfg->setUseHashME(false);
            break;
          }
        }
        refPic->addPictureToHashMapForInter();
      }
    }
  }
}

void EncGOP::xPicInitRateControl(int &estimatedBits, int gopId, double &lambda, Picture *pic, Slice *slice)
{
  if ( !m_pcCfg->getUseRateCtrl() ) // TODO: does this work with multiple slices and slice-segments?
  {
    return;
  }
  int frameLevel = m_pcRateCtrl->getRCSeq()->getGOPID2Level( gopId );
  if ( pic->slices[0]->isIRAP() )
  {
    frameLevel = 0;
  }
  m_pcRateCtrl->initRCPic( frameLevel );
  estimatedBits = m_pcRateCtrl->getRCPic()->getTargetBits();
Vadim Seregin's avatar
Vadim Seregin committed

#if U0132_TARGET_BITS_SATURATION
  if (m_pcRateCtrl->getCpbSaturationEnabled() && frameLevel != 0)
  {
    int estimatedCpbFullness = m_pcRateCtrl->getCpbState() + m_pcRateCtrl->getBufferingRate();
Vadim Seregin's avatar
Vadim Seregin committed

    // prevent overflow
    if (estimatedCpbFullness - estimatedBits > (int)(m_pcRateCtrl->getCpbSize()*0.9f))
    {
      estimatedBits = estimatedCpbFullness - (int)(m_pcRateCtrl->getCpbSize()*0.9f);
    }
Vadim Seregin's avatar
Vadim Seregin committed

    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
Vadim Seregin's avatar
Vadim Seregin committed

    m_pcRateCtrl->getRCPic()->setTargetBits(estimatedBits);
  }
#endif
Vadim Seregin's avatar
Vadim Seregin committed

  int sliceQP = m_pcCfg->getInitialQP();
  if ( ( slice->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 * (slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA) - 8
                                - DISTORTION_PRECISION_ADJUSTMENT(slice->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->calCostPictureI(pic);
    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 );
Vadim Seregin's avatar
Vadim Seregin committed

#if U0132_TARGET_BITS_SATURATION
      if (m_pcRateCtrl->getCpbSaturationEnabled() )
      {
        int estimatedCpbFullness = m_pcRateCtrl->getCpbState() + m_pcRateCtrl->getBufferingRate();
Vadim Seregin's avatar
Vadim Seregin committed

        // prevent overflow
        if (estimatedCpbFullness - bits > (int)(m_pcRateCtrl->getCpbSize()*0.9f))
        {
          bits = estimatedCpbFullness - (int)(m_pcRateCtrl->getCpbSize()*0.9f);
        }
Vadim Seregin's avatar
Vadim Seregin committed

        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
Vadim Seregin's avatar
Vadim Seregin committed

      if ( bits < 200 )
      {
        bits = 200;
      }
      m_pcRateCtrl->getRCPic()->setTargetBits( bits );
    }
Vadim Seregin's avatar
Vadim Seregin committed

    list<EncRCPic*> listPreviousPicture = m_pcRateCtrl->getPicList();
    m_pcRateCtrl->getRCPic()->getLCUInitTargetBits();
    lambda  = m_pcRateCtrl->getRCPic()->estimatePicLambda( listPreviousPicture, slice->isIRAP());
    sliceQP = m_pcRateCtrl->getRCPic()->estimatePicQP( lambda, listPreviousPicture );
  }
  else    // normal case
  {
    list<EncRCPic*> listPreviousPicture = m_pcRateCtrl->getPicList();
    lambda  = m_pcRateCtrl->getRCPic()->estimatePicLambda( listPreviousPicture, slice->isIRAP());
    sliceQP = m_pcRateCtrl->getRCPic()->estimatePicQP( lambda, listPreviousPicture );
  }
Vadim Seregin's avatar
Vadim Seregin committed

  sliceQP = Clip3( -slice->getSPS()->getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, sliceQP );
  m_pcRateCtrl->getRCPic()->setPicEstQP( sliceQP );
Vadim Seregin's avatar
Vadim Seregin committed

  m_pcSliceEncoder->resetQP( pic, sliceQP, lambda );
}

Brian Heng's avatar
Brian Heng committed
void EncGOP::xPicInitLMCS(Picture *pic, PicHeader *picHeader, Slice *slice)
  if (slice->getSPS()->getUseLmcs())
    const SliceType realSliceType = slice->getSliceType();
    SliceType condSliceType = realSliceType;

    if (condSliceType != I_SLICE && slice->getNalUnitLayerId() > 0 && (slice->getNalUnitType()>= NAL_UNIT_CODED_SLICE_IDR_W_RADL && slice->getNalUnitType()<= NAL_UNIT_CODED_SLICE_CRA))
    {
      condSliceType = I_SLICE;
    }
    m_pcReshaper->getReshapeCW()->rspTid = slice->getTLayer() + (slice->isIntra() ? 0 : 1);
    m_pcReshaper->getReshapeCW()->rspSliceQP = slice->getSliceQp();
Vadim Seregin's avatar
Vadim Seregin committed

    m_pcReshaper->setSrcReshaped(false);
    m_pcReshaper->setRecReshaped(true);
Vadim Seregin's avatar
Vadim Seregin committed

    m_pcReshaper->getSliceReshaperInfo().chrResScalingOffset = m_pcCfg->getReshapeCSoffset();

    if (m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_PQ)
    {
      m_pcReshaper->preAnalyzerHDR(pic, condSliceType, m_pcCfg->getReshapeCW(), m_pcCfg->getDualITree());
    }
    else if (m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_SDR || m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_HLG)
    {
      m_pcReshaper->preAnalyzerLMCS(pic, m_pcCfg->getReshapeSignalType(), condSliceType, m_pcCfg->getReshapeCW());
    }
    else
    {
      THROW("Reshaper for other signal currently not defined!");
    }
Vadim Seregin's avatar
Vadim Seregin committed

    if (condSliceType == I_SLICE )
    {
      if (m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_PQ)
      {
        m_pcReshaper->initLUTfromdQPModel();
        m_pcEncLib->getRdCost()->updateReshapeLumaLevelToWeightTableChromaMD(m_pcReshaper->getInvLUT());
      }
      else if (m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_SDR || m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_HLG)
      {
        if (m_pcReshaper->getReshapeFlag())
        {
          m_pcReshaper->constructReshaperLMCS();
          m_pcEncLib->getRdCost()->updateReshapeLumaLevelToWeightTable(m_pcReshaper->getSliceReshaperInfo(), m_pcReshaper->getWeightTable(), m_pcReshaper->getCWeight());
        }
      }
      else
      {
        THROW("Reshaper for other signal currently not defined!");
      }
Vadim Seregin's avatar
Vadim Seregin committed

      if (realSliceType != condSliceType)
      {
        m_pcReshaper->setCTUFlag(true);
      }
    }
    else
    {
      if (!m_pcReshaper->getReshapeFlag())
      {
        m_pcReshaper->setCTUFlag(false);
      }
      else
        m_pcReshaper->setCTUFlag(true);
Vadim Seregin's avatar
Vadim Seregin committed

      m_pcReshaper->getSliceReshaperInfo().setSliceReshapeModelPresentFlag(false);
Vadim Seregin's avatar
Vadim Seregin committed

      if (m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_PQ)
      {
        m_pcEncLib->getRdCost()->restoreReshapeLumaLevelToWeightTable();
      }
      else if (m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_SDR || m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_HLG)
      {
        int modIP = pic->getPOC() - pic->getPOC() / m_pcCfg->getReshapeCW().rspFpsToIp * m_pcCfg->getReshapeCW().rspFpsToIp;
Seungwook Hong's avatar
Seungwook Hong committed
#if JVET_Z0118_GDR
Seungwook Hong's avatar
Seungwook Hong committed
        if (m_pcCfg->getGdrEnabled() && slice->isInterGDR())
Seungwook Hong's avatar
Seungwook Hong committed
        {
          modIP = 0;
        }
#endif
        if (m_pcReshaper->getReshapeFlag() && m_pcCfg->getReshapeCW().updateCtrl == 2 && modIP == 0)
        {
          m_pcReshaper->getSliceReshaperInfo().setSliceReshapeModelPresentFlag(true);
          m_pcReshaper->constructReshaperLMCS();
          m_pcEncLib->getRdCost()->updateReshapeLumaLevelToWeightTable(m_pcReshaper->getSliceReshaperInfo(), m_pcReshaper->getWeightTable(), m_pcReshaper->getCWeight());
        }
      }
      else
      {
        THROW("Reshaper for other signal currently not defined!");
      }
    }
Vadim Seregin's avatar
Vadim Seregin committed

Brian Heng's avatar
Brian Heng committed
    //set all necessary information in LMCS APS and picture header
    picHeader->setLmcsEnabledFlag(m_pcReshaper->getSliceReshaperInfo().getUseSliceReshaper());
    slice->setLmcsEnabledFlag(m_pcReshaper->getSliceReshaperInfo().getUseSliceReshaper());
Brian Heng's avatar
Brian Heng committed
    picHeader->setLmcsChromaResidualScaleFlag(m_pcReshaper->getSliceReshaperInfo().getSliceReshapeChromaAdj() == 1);
    if (m_pcReshaper->getSliceReshaperInfo().getSliceReshapeModelPresentFlag())
    {
      int apsId = std::min<int>( 3, m_pcEncLib->getVPS() == nullptr ? 0 : m_pcEncLib->getVPS()->getGeneralLayerIdx( m_pcEncLib->getLayerId() ) );
Brian Heng's avatar
Brian Heng committed
      picHeader->setLmcsAPSId(apsId);
      APS* lmcsAPS = picHeader->getLmcsAPS();
      if (lmcsAPS == nullptr)
      {
        ParameterSetMap<APS> *apsMap = m_pcEncLib->getApsMap();
        lmcsAPS = apsMap->getPS((apsId << NUM_APS_TYPE_LEN) + LMCS_APS);
        if (lmcsAPS == NULL)
        {
          lmcsAPS = apsMap->allocatePS((apsId << NUM_APS_TYPE_LEN) + LMCS_APS);
          lmcsAPS->setAPSId(apsId);
          lmcsAPS->setAPSType(LMCS_APS);
        }
Brian Heng's avatar
Brian Heng committed
        picHeader->setLmcsAPS(lmcsAPS);
      }
      //m_pcReshaper->copySliceReshaperInfo(lmcsAPS->getReshaperAPSInfo(), m_pcReshaper->getSliceReshaperInfo());
      SliceReshapeInfo& tInfo = lmcsAPS->getReshaperAPSInfo();
      SliceReshapeInfo& sInfo = m_pcReshaper->getSliceReshaperInfo();
      tInfo.reshaperModelMaxBinIdx = sInfo.reshaperModelMaxBinIdx;
      tInfo.reshaperModelMinBinIdx = sInfo.reshaperModelMinBinIdx;
      memcpy(tInfo.reshaperModelBinCWDelta, sInfo.reshaperModelBinCWDelta, sizeof(int)*(PIC_CODE_CW_BINS));
      tInfo.maxNbitsNeededDeltaCW = sInfo.maxNbitsNeededDeltaCW;
      tInfo.chrResScalingOffset = sInfo.chrResScalingOffset;
      m_pcEncLib->getApsMap()->setChangedFlag((lmcsAPS->getAPSId() << NUM_APS_TYPE_LEN) + LMCS_APS);
    }
Brian Heng's avatar
Brian Heng committed
    if (picHeader->getLmcsEnabledFlag())
      int apsId = std::min<int>( 3, m_pcEncLib->getVPS() == nullptr ? 0 : m_pcEncLib->getVPS()->getGeneralLayerIdx( m_pcEncLib->getLayerId() ) );
Brian Heng's avatar
Brian Heng committed
      picHeader->setLmcsAPSId(apsId);
Vadim Seregin's avatar
Vadim Seregin committed

#if JVET_V0094_BILATERAL_FILTER
Vadim Seregin's avatar
Vadim Seregin committed
class BIFCabacEstImp : public BIFCabacEst
{
  CABACWriter* CABACEstimator;
public:
  BIFCabacEstImp(CABACWriter* _CABACEstimator) : CABACEstimator(_CABACEstimator) {};
  virtual ~BIFCabacEstImp() {};

#if JVET_AG0196_CABAC_RETRAIN
  virtual uint64_t getBits( const ComponentID compID, Slice& slice, const BifParams& htdfParams )
#else
  virtual uint64_t getBits(const ComponentID compID, const Slice& slice, const BifParams& htdfParams)
Vadim Seregin's avatar
Vadim Seregin committed
  {
    CABACEstimator->initCtxModels(slice);
    CABACEstimator->resetBits();
    CABACEstimator->bif( compID, slice, htdfParams);
    return CABACEstimator->getEstFracBits();
  }
Vadim Seregin's avatar
Vadim Seregin committed

// ====================================================================================================================
// Public member functions
// ====================================================================================================================
void EncGOP::compressGOP(int iPOCLast, int iNumPicRcvd, PicList &rcListPic, std::list<PelUnitBuf *> &rcListPicYuvRecOut,
                         bool isField, bool isTff, const InputColourSpaceConversion snr_conversion,
                         const bool printFrameMSE,
#if MSSIM_UNIFORM_METRICS_LOG
                         const bool printMSSSIM,
#endif
                         bool isEncodeLtRef, const int picIdInGOP)
Brian Heng's avatar
Brian Heng committed
  PicHeader*      picHeader = NULL;
  Slice*      pcSlice;
  OutputBitstream  *pcBitstreamRedirect;
  pcBitstreamRedirect = new OutputBitstream;
  AccessUnit::iterator  itLocationToPushSliceHeaderNALU; // used to store location where NALU containing slice header is to be inserted
Vadim Seregin's avatar
Vadim Seregin committed
  Picture* scaledRefPic[MAX_NUM_REF] = {};
  xInitGOP( iPOCLast, iNumPicRcvd, isField, isEncodeLtRef );

  m_iNumPicCoded = 0;
  SEIMessages leadingSeiMessages;
  SEIMessages nestedSeiMessages;
  SEIMessages duInfoSeiMessages;
  SEIMessages trailingSeiMessages;
  std::deque<DUData> duData;

  EfficientFieldIRAPMapping effFieldIRAPMap;
  if (m_pcCfg->getEfficientFieldIRAPEnabled())
  {
    effFieldIRAPMap.initialize(isField, m_iGopSize, iPOCLast, iNumPicRcvd, m_iLastIDR, this, m_pcCfg);
  }

Haiwei Sun's avatar
Haiwei Sun committed
  if( isField && picIdInGOP == 0 )
  {
    for( int iGOPid = 0; iGOPid < max(2, m_iGopSize); iGOPid++ )
    {
      m_pcCfg->setEncodedFlag( iGOPid, false );
    }
  }
  for( int iGOPid = picIdInGOP; iGOPid <= picIdInGOP; iGOPid++ )
  {
    // reset flag indicating whether pictures have been encoded
    m_pcCfg->setEncodedFlag( iGOPid, false );
    if (m_pcCfg->getEfficientFieldIRAPEnabled())
    {
      iGOPid=effFieldIRAPMap.adjustGOPid(iGOPid);
    }

    //-- For time output for each slice
    auto beforeTime = std::chrono::steady_clock::now();

#if !X0038_LAMBDA_FROM_QP_CAPABILITY
    uint32_t uiColDir = calculateCollocatedFromL1Flag(m_pcCfg, iGOPid, m_iGopSize);
#endif

    /////////////////////////////////////////////////////////////////////////////////////////////////// Initial to start encoding
    int iTimeOffset;
    int pocCurr;
    int multipleFactor = m_pcCfg->getUseCompositeRef() ? 2 : 1;

    if(iPOCLast == 0) //case first frame or first top field
    {
      pocCurr=0;
      iTimeOffset = isField ? (1 - multipleFactor) : multipleFactor;
    }
    else if(iPOCLast == 1 && isField) //case first bottom field, just like the first frame, the poc computation is not right anymore, we set the right value
    {
      pocCurr = 1;
      pocCurr = iPOCLast - iNumPicRcvd * multipleFactor + m_pcCfg->getGOPEntry(iGOPid).m_POC - ((isField && m_iGopSize>1) ? 1 : 0);
    if (m_pcCfg->getUseCompositeRef() && isEncodeLtRef)
    {
      pocCurr++;
      iTimeOffset--;
    }
    if (pocCurr / multipleFactor >= m_pcCfg->getFramesToBeEncoded())
    {
      if (m_pcCfg->getEfficientFieldIRAPEnabled())
      {
        iGOPid=effFieldIRAPMap.restoreGOPid(iGOPid);
      }
      continue;
    }

    if( getNalUnitType(pocCurr, m_iLastIDR, isField) == NAL_UNIT_CODED_SLICE_IDR_W_RADL || getNalUnitType(pocCurr, m_iLastIDR, isField) == NAL_UNIT_CODED_SLICE_IDR_N_LP )
    {
      m_iLastIDR = pocCurr;
    }

    // start a new access unit: create an entry in the list of output access units
    AccessUnit accessUnit;
    accessUnit.temporalId = m_pcCfg->getGOPEntry( iGOPid ).m_temporalId;
    xGetBuffer( rcListPic, rcListPicYuvRecOut,
                iNumPicRcvd, iTimeOffset, pcPic, pocCurr, isField );
Brian Heng's avatar
Brian Heng committed
    picHeader = pcPic->cs->picHeader;
    picHeader->setSPSId( pcPic->cs->pps->getSPSId() );
    picHeader->setPPSId( pcPic->cs->pps->getPPSId() );
    picHeader->setMinQTSizes(pcPic->cs->sps->getMinQTSizes());
    picHeader->setMaxMTTHierarchyDepths(pcPic->cs->sps->getMaxMTTHierarchyDepths());
    picHeader->setMaxBTSizes(pcPic->cs->sps->getMaxBTSizes());
    picHeader->setMaxTTSizes(pcPic->cs->sps->getMaxTTSizes());
Brian Heng's avatar
Brian Heng committed
    picHeader->setSplitConsOverrideFlag(false);
    // initial two flags to be false
    picHeader->setPicInterSliceAllowedFlag(false);
    picHeader->setPicIntraSliceAllowedFlag(false);
#if ER_CHROMA_QP_WCG_PPS
    // th this is a hot fix for the choma qp control
    if( m_pcEncLib->getWCGChromaQPControl().isEnabled() && m_pcEncLib->getSwitchPOC() != -1 )
    {
      static int usePPS = 0; /* TODO: MT */
      if( pocCurr == m_pcEncLib->getSwitchPOC() )
      {
        usePPS = 1;
      }
      const PPS *pPPS = m_pcEncLib->getPPS(usePPS);
      // replace the pps with a more appropriated one
      pcPic->cs->pps = pPPS;
    }
    // create objects based on the picture size
    const int picWidth = pcPic->cs->pps->getPicWidthInLumaSamples();
    const int picHeight = pcPic->cs->pps->getPicHeightInLumaSamples();
    const int maxCUWidth = pcPic->cs->sps->getMaxCUWidth();
    const int maxCUHeight = pcPic->cs->sps->getMaxCUHeight();
    const ChromaFormat chromaFormatIDC = pcPic->cs->sps->getChromaFormatIdc();
    const int maxTotalCUDepth = floorLog2(maxCUWidth) - pcPic->cs->sps->getLog2MinCodingBlockSize();

    m_pcSliceEncoder->create( picWidth, picHeight, chromaFormatIDC, maxCUWidth, maxCUHeight, maxTotalCUDepth );

#if ENABLE_SPLIT_PARALLELISM
    pcPic->scheduler.init( pcPic->cs->pcv->heightInCtus, pcPic->cs->pcv->widthInCtus, 1                          , 0                             , m_pcCfg->getNumSplitThreads() );
#endif
#if JVET_Y0240_BIM
    const bool isCurrentFrameFiltered = m_pcCfg->getGopBasedTemporalFilterEnabled() || m_pcCfg->getBIM();
#else
    const bool isCurrentFrameFiltered = m_pcCfg->getGopBasedTemporalFilterEnabled();
#endif
    const SPS& sps = *pcPic->cs->sps;
    pcPic->createTempBuffers(pcPic->cs->pps->pcv->maxCUWidth, isCurrentFrameFiltered, m_pcEncLib->isResChangeInClvsEnabled(), false);
    pcPic->getTrueOrigBuf().copyFrom(pcPic->getOrigBuf());
    if (m_pcEncLib->isResChangeInClvsEnabled())
    {
      pcPic->M_BUFS(0, PIC_TRUE_ORIGINAL_INPUT).copyFrom(pcPic->M_BUFS(0, PIC_ORIGINAL_INPUT));
    }
    if(isCurrentFrameFiltered)
    {
      if (m_pcEncLib->isResChangeInClvsEnabled())
      {
        m_pcEncLib->getTemporalFilter().filter(pcPic->M_BUFS(0, PIC_ORIGINAL_INPUT), pocCurr);

        const Window& curScalingWindow = pcPic->getScalingWindow();
        const int curPicWidth = pcPic->M_BUFS(0, PIC_ORIGINAL).Y().width - SPS::getWinUnitX(sps.getChromaFormatIdc()) * (curScalingWindow.getWindowLeftOffset() + curScalingWindow.getWindowRightOffset());
        const int curPicHeight = pcPic->M_BUFS(0, PIC_ORIGINAL).Y().height - SPS::getWinUnitY(sps.getChromaFormatIdc()) * (curScalingWindow.getWindowTopOffset() + curScalingWindow.getWindowBottomOffset());
        
        const PPS* pps = m_pcEncLib->getPPS(0);
        const Window& refScalingWindow = pps->getScalingWindow();
        const int refPicWidth = pcPic->M_BUFS(0, PIC_ORIGINAL_INPUT).Y().width - SPS::getWinUnitX(sps.getChromaFormatIdc()) * (refScalingWindow.getWindowLeftOffset() + refScalingWindow.getWindowRightOffset());
        const int refPicHeight = pcPic->M_BUFS(0, PIC_ORIGINAL_INPUT).Y().height - SPS::getWinUnitY(sps.getChromaFormatIdc()) * (refScalingWindow.getWindowTopOffset() + refScalingWindow.getWindowBottomOffset());
        const int xScale = ((refPicWidth << SCALE_RATIO_BITS) + (curPicWidth >> 1)) / curPicWidth;
        const int yScale = ((refPicHeight << SCALE_RATIO_BITS) + (curPicHeight >> 1)) / curPicHeight;
Loading
Loading full blame...