Skip to content
Snippets Groups Projects
EncGOP.cpp 156 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     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 "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_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));
      m_lastBPSEI           = 0;
      m_bufferingPeriodSEIPresentInAU = false;
      m_associatedIRAPType  = NAL_UNIT_CODED_SLICE_IDR_N_LP;
      m_associatedIRAPPOC   = 0;
    #if W0038_DB_OPT
      m_pcDeblockingTempPicYuv = NULL;
    #endif
    
    
    #if JVET_O0756_CALCULATE_HDRMETRICS
      
      m_ppcFrameOrg = NULL;
      m_ppcFrameRec = NULL;
      
      m_pcConvertFormat = NULL;
      m_pcConvertIQuantize = NULL;
      m_pcColorTransform = NULL;
      m_pcDistortionDeltaE = NULL;
      m_pcTransferFct = NULL;
      
      m_pcColorTransformParams = NULL;
      m_pcFrameFormat = NULL;
      
      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 = NULL;
      
      delete m_pcConvertFormat;
      delete m_pcConvertIQuantize;
      delete m_pcColorTransform;
      delete m_pcDistortionDeltaE;
      delete m_pcTransferFct;
      
      m_pcConvertFormat = NULL;
      m_pcConvertIQuantize = NULL;
      m_pcColorTransform = NULL;
      m_pcDistortionDeltaE = NULL;
      m_pcTransferFct = NULL;
      
      delete m_pcColorTransformParams;
      delete m_pcFrameFormat;
      
      m_pcColorTransformParams = NULL;
      m_pcFrameFormat = NULL;
      
    #endif
    
    }
    
    /** Create list to contain pointers to CTU start addresses of slice.
     */
    void  EncGOP::create()
    {
      m_bLongtermTestPictureHasBeenCoded = 0;
      m_bLongtermTestPictureHasBeenCoded2 = 0;
    }
    
    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;
      }
    
    }
    
    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();
      m_pcRateCtrl           = pcEncLib->getRateCtrl();
      m_lastBPSEI          = 0;
      m_totalCoded         = 0;
    
      m_AUWriterIf = pcEncLib->getAUWriterIf();
    
    Taoran Lu's avatar
    Taoran Lu committed
      if (m_pcCfg->getReshaper())
    
      {
        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->getReshaper() && m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_PQ )
      {
        alfWSSD = 1;
      }
      pcEncLib->getALF()->setAlfWSSD(alfWSSD);
    
    Taoran Lu's avatar
    Taoran Lu committed
    #endif
      m_pcReshaper = pcEncLib->getReshaper();
    
      
    #if JVET_O0756_CALCULATE_HDRMETRICS
      const bool calculateHdrMetrics = m_pcEncLib->getCalcluateHdrMetrics();
      if(calculateHdrMetrics){
      //allocate frame buffers and initialize class members
      int chainNumber = 5;
      
      m_ppcFrameOrg = new hdrtoolslib::Frame* [chainNumber];
      m_ppcFrameRec = new hdrtoolslib::Frame* [chainNumber];
      
      double* whitePointDeltaE = new double[hdrtoolslib::NB_REF_WHITE];
      for (int i=0; i<hdrtoolslib::NB_REF_WHITE; i++) {
        whitePointDeltaE[i] = m_pcCfg->getWhitePointDeltaE(i);
      }
      double dMaxSampleValue = m_pcCfg->getMaxSampleValue();
      hdrtoolslib::SampleRange sampleRange = m_pcCfg->getSampleRange();
      hdrtoolslib::ChromaFormat chFmt = hdrtoolslib::ChromaFormat(m_pcCfg->getChromaFormatIdc());
      int iBitDepth = m_pcCfg->getBitDepth(CHANNEL_TYPE_LUMA);
      hdrtoolslib::ColorPrimaries colorPrimaries = m_pcCfg->getColorPrimaries();
      bool bEnableTFunctionLUT = m_pcCfg->getEnableTFunctionLUT();
      hdrtoolslib::ChromaLocation* chromaLocation = new hdrtoolslib::ChromaLocation[2];
      for (int i=0; i<2; i++) {
        chromaLocation[i] = m_pcCfg->getChromaLocation(i);
      }
      int iChromaUpFilter  = m_pcCfg->getChromaUPFilter();
      int cropOffsetLeft   = m_pcCfg->getCropOffsetLeft();
      int cropOffsetTop    = m_pcCfg->getCropOffsetTop();
      int cropOffsetRight  = m_pcCfg->getCropOffsetRight();
      int cropOffsetBottom = m_pcCfg->getCropOffsetBottom();
      
      int iWidth = m_pcCfg->getSourceWidth() - cropOffsetLeft + cropOffsetRight;
      int iHeight = m_pcCfg->getSourceHeight() - cropOffsetTop  + cropOffsetBottom;
      
      m_ppcFrameOrg[0] = new hdrtoolslib::Frame(iWidth, iHeight, false, hdrtoolslib::CM_YCbCr, colorPrimaries, chFmt, sampleRange, iBitDepth, false, hdrtoolslib::TF_PQ, 0);
      m_ppcFrameRec[0] = new hdrtoolslib::Frame(iWidth, iHeight, false, hdrtoolslib::CM_YCbCr, colorPrimaries, chFmt, sampleRange, iBitDepth, false, hdrtoolslib::TF_PQ, 0);                                                                   // Orginal & Reconstructed 4:2:0
      
      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, iBitDepth, 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, iBitDepth, false, hdrtoolslib::TF_PQ, 0);                                // 420 to 444 conversion
      
      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
      
      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
      
      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
      
      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;
      
      m_pcConvertFormat = hdrtoolslib::ConvertColorFormat::create(iWidth, iHeight, chFmt, hdrtoolslib::CF_444, iChromaUpFilter, 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, dMaxSampleValue, whitePointDeltaE, 1);
      m_pcTransferFct = hdrtoolslib::TransferFunction::create(hdrtoolslib::TF_PQ, true, dMaxSampleValue, 0, 0.0, 1.0, bEnableTFunctionLUT);
      }
    #endif
    
    }
    
    int EncGOP::xWriteVPS (AccessUnit &accessUnit, const VPS *vps)
    {
      OutputNALUnit nalu(NAL_UNIT_VPS);
      m_HLSWriter->setBitstream( &nalu.m_Bitstream );
      m_HLSWriter->codeVPS( vps );
      accessUnit.push_back(new NALUnitEBSP(nalu));
      return (int)(accessUnit.back()->m_nalUnitData.str().size()) * 8;
    }
    
    
    int EncGOP::xWriteDPS (AccessUnit &accessUnit, const DPS *dps)
    {
      if (dps->getDecodingParameterSetId() !=0)
      {
        OutputNALUnit nalu(NAL_UNIT_DPS);
        m_HLSWriter->setBitstream( &nalu.m_Bitstream );
        m_HLSWriter->codeDPS( dps );
        accessUnit.push_back(new NALUnitEBSP(nalu));
        return (int)(accessUnit.back()->m_nalUnitData.str().size()) * 8;
      }
      else
      {
        return 0;
      }
    }
    
    
    int EncGOP::xWriteSPS (AccessUnit &accessUnit, const SPS *sps)
    {
      OutputNALUnit nalu(NAL_UNIT_SPS);
      m_HLSWriter->setBitstream( &nalu.m_Bitstream );
      m_HLSWriter->codeSPS( sps );
      accessUnit.push_back(new NALUnitEBSP(nalu));
      return (int)(accessUnit.back()->m_nalUnitData.str().size()) * 8;
    
    }
    
    
    #if JVET_O1136_TS_BDPCM_SIGNALLING
    int EncGOP::xWritePPS (AccessUnit &accessUnit, const PPS *pps, const SPS *sps)
    #else
    
    int EncGOP::xWritePPS (AccessUnit &accessUnit, const PPS *pps)
    
    {
      OutputNALUnit nalu(NAL_UNIT_PPS);
      m_HLSWriter->setBitstream( &nalu.m_Bitstream );
    
    #if JVET_O1136_TS_BDPCM_SIGNALLING
      m_HLSWriter->codePPS( pps, sps );
    #else
    
      accessUnit.push_back(new NALUnitEBSP(nalu));
      return (int)(accessUnit.back()->m_nalUnitData.str().size()) * 8;
    }
    
    
    Hendry's avatar
    Hendry committed
    int EncGOP::xWriteAPS(AccessUnit &accessUnit, APS *aps)
    {
      OutputNALUnit nalu(NAL_UNIT_APS);
      m_HLSWriter->setBitstream(&nalu.m_Bitstream);
      m_HLSWriter->codeAPS(aps);
      accessUnit.push_back(new NALUnitEBSP(nalu));
      return (int)(accessUnit.back()->m_nalUnitData.str().size()) * 8;
    }
    
    
    int EncGOP::xWriteParameterSets (AccessUnit &accessUnit, Slice *slice, const bool bSeqFirst)
    {
      int actualTotalBits = 0;
    
      if (bSeqFirst)
      {
        actualTotalBits += xWriteVPS(accessUnit, m_pcEncLib->getVPS());
      }
    
      if (bSeqFirst)
      {
        actualTotalBits += xWriteDPS(accessUnit, m_pcEncLib->getDPS());
      }
    
    
      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());
      }
      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_O1136_TS_BDPCM_SIGNALLING
        actualTotalBits += xWritePPS(accessUnit, slice->getPPS(), slice->getSPS());
    #else
    
        actualTotalBits += xWritePPS(accessUnit, slice->getPPS());
    
      }
    
      return actualTotalBits;
    }
    
    void EncGOP::xWriteAccessUnitDelimiter (AccessUnit &accessUnit, Slice *slice)
    {
      AUDWriter audWriter;
      OutputNALUnit nalu(NAL_UNIT_ACCESS_UNIT_DELIMITER);
    
      int picType = slice->isIntra() ? 0 : (slice->isInterP() ? 1 : 2);
    
      audWriter.codeAUD(nalu.m_Bitstream, 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, const SPS *sps)
    {
      // don't do anything, if we get an empty list
      if (seiMessages.empty())
      {
        return;
      }
      OutputNALUnit nalu(naluType, temporalId);
      m_seiWriter.writeSEImessages(nalu.m_Bitstream, seiMessages, sps, false);
      auPos = accessUnit.insert(auPos, new NALUnitEBSP(nalu));
      auPos++;
    }
    
    void EncGOP::xWriteSEISeparately (NalUnitType naluType, SEIMessages& seiMessages, AccessUnit &accessUnit, AccessUnit::iterator &auPos, int temporalId, const SPS *sps)
    {
      // 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, temporalId);
        m_seiWriter.writeSEImessages(nalu.m_Bitstream, tmpMessages, sps, false);
        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, const SPS *sps, 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_DPS
    
        || (*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)
    
      // Active parameter sets SEI must always be the first SEI
      currentMessages = extractSeisByType(localMessages, SEI::ACTIVE_PARAMETER_SETS);
      CHECK(!(currentMessages.size() <= 1), "Unspecified error");
      xWriteSEI(NAL_UNIT_PREFIX_SEI, currentMessages, accessUnit, itNalu, temporalId, sps);
      xClearSEIs(currentMessages, !testWrite);
    
      // 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, sps);
      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, sps);
      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, sps);
        xClearSEIs(currentMessages, !testWrite);
      }
    
      // 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, sps);
      xClearSEIs(currentMessages, !testWrite);
    
      // And finally everything else one by one
      xWriteSEISeparately(NAL_UNIT_PREFIX_SEI, localMessages, accessUnit, itNalu, temporalId, sps);
      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, sps, true);
      // update Timing and DU info SEI
      xUpdateDuData(testAU, duData);
      xUpdateTimingSEI(picTiming, duData, sps);
      xUpdateDuInfoSEI(duInfoSeiMessages, picTiming);
      // actual writing
      xWriteLeadingSEIOrdered(seiMessages, duInfoSeiMessages, accessUnit, temporalId, sps, false);
    
      // testAU will automatically be cleaned up when losing scope
    }
    
    void EncGOP::xWriteTrailingSEIMessages (SEIMessages& seiMessages, AccessUnit &accessUnit, int temporalId, const SPS *sps)
    {
      // 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, sps);
      deleteSEIs(seiMessages);
    }
    
    void EncGOP::xWriteDuSEIMessages (SEIMessages& duInfoSeiMessages, AccessUnit &accessUnit, int temporalId, const SPS *sps, std::deque<DUData> &duData)
    {
    
    Virginie Drugeon's avatar
    Virginie Drugeon committed
      const HRDParameters *hrd = sps->getHrdParameters();
    
    
      if( m_pcCfg->getDecodingUnitInfoSEIEnabled() && hrd->getSubPicCpbParamsPresentFlag() )
      {
        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, sps);
          // 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->getActiveParameterSetsSEIEnabled())
      {
        SEIActiveParameterSets *sei = new SEIActiveParameterSets;
        m_seiEncoder.initSEIActiveParameterSets(sei, sps);
        seiMessages.push_back(sei);
      }
    
      if(m_pcCfg->getFramePackingArrangementSEIEnabled())
      {
        SEIFramePacking *sei = new SEIFramePacking;
        m_seiEncoder.initSEIFramePacking (sei, m_iNumPicCoded);
        seiMessages.push_back(sei);
      }
    
      if(m_pcCfg->getSegmentedRectFramePackingArrangementSEIEnabled())
      {
        SEISegmentedRectFramePacking *sei = new SEISegmentedRectFramePacking;
        m_seiEncoder.initSEISegmentedRectFramePacking(sei);
        seiMessages.push_back(sei);
      }
    
      if (m_pcCfg->getDisplayOrientationSEIAngle())
      {
        SEIDisplayOrientation *sei = new SEIDisplayOrientation;
        m_seiEncoder.initSEIDisplayOrientation(sei);
        seiMessages.push_back(sei);
      }
    
      if(m_pcCfg->getToneMappingInfoSEIEnabled())
      {
        SEIToneMappingInfo *sei = new SEIToneMappingInfo;
        m_seiEncoder.initSEIToneMappingInfo (sei);
        seiMessages.push_back(sei);
      }
    
      if(m_pcCfg->getTMCTSSEIEnabled())
      {
        SEITempMotionConstrainedTileSets *sei = new SEITempMotionConstrainedTileSets;
        m_seiEncoder.initSEITempMotionConstrainedTileSets(sei, pps);
        seiMessages.push_back(sei);
      }
    
      if(m_pcCfg->getTimeCodeSEIEnabled())
      {
        SEITimeCode *seiTimeCode = new SEITimeCode;
        m_seiEncoder.initSEITimeCode(seiTimeCode);
        seiMessages.push_back(seiTimeCode);
      }
    
      if(m_pcCfg->getKneeSEIEnabled())
      {
        SEIKneeFunctionInfo *sei = new SEIKneeFunctionInfo;
        m_seiEncoder.initSEIKneeFunctionInfo(sei);
        seiMessages.push_back(sei);
      }
    
      if(m_pcCfg->getMasteringDisplaySEI().colourVolumeSEIEnabled)
      {
        const SEIMasteringDisplay &seiCfg=m_pcCfg->getMasteringDisplaySEI();
        SEIMasteringDisplayColourVolume *sei = new SEIMasteringDisplayColourVolume;
        sei->values = seiCfg;
        seiMessages.push_back(sei);
      }
      if(m_pcCfg->getChromaResamplingFilterHintEnabled())
      {
        SEIChromaResamplingFilterHint *seiChromaResamplingFilterHint = new SEIChromaResamplingFilterHint;
        m_seiEncoder.initSEIChromaResamplingFilterHint(seiChromaResamplingFilterHint, m_pcCfg->getChromaResamplingHorFilterIdc(), m_pcCfg->getChromaResamplingVerFilterIdc());
        seiMessages.push_back(seiChromaResamplingFilterHint);
      }
    #if U0033_ALTERNATIVE_TRANSFER_CHARACTERISTICS_SEI
      if(m_pcCfg->getSEIAlternativeTransferCharacteristicsSEIEnable())
      {
        SEIAlternativeTransferCharacteristics *seiAlternativeTransferCharacteristics = new SEIAlternativeTransferCharacteristics;
        m_seiEncoder.initSEIAlternativeTransferCharacteristics(seiAlternativeTransferCharacteristics);
        seiMessages.push_back(seiAlternativeTransferCharacteristics);
      }
    #endif
    }
    
    void EncGOP::xCreatePerPictureSEIMessages (int picInGOP, SEIMessages& seiMessages, SEIMessages& nestedSeiMessages, Slice *slice)
    {
    
    Virginie Drugeon's avatar
    Virginie Drugeon committed
      if( ( m_pcCfg->getBufferingPeriodSEIEnabled() ) && ( slice->getSliceType() == I_SLICE ) &&
        ( slice->getSPS()->getVuiParametersPresentFlag() ) &&
        ( ( slice->getSPS()->getHrdParameters()->getNalHrdParametersPresentFlag() )
        || ( slice->getSPS()->getHrdParameters()->getVclHrdParametersPresentFlag() ) ) )
    
      {
        SEIBufferingPeriod *bufferingPeriodSEI = new SEIBufferingPeriod();
        m_seiEncoder.initSEIBufferingPeriod(bufferingPeriodSEI, slice);
        seiMessages.push_back(bufferingPeriodSEI);
        m_bufferingPeriodSEIPresentInAU = true;
    
        if (m_pcCfg->getScalableNestingSEIEnabled())
        {
          SEIBufferingPeriod *bufferingPeriodSEIcopy = new SEIBufferingPeriod();
          bufferingPeriodSEI->copyTo(*bufferingPeriodSEIcopy);
          nestedSeiMessages.push_back(bufferingPeriodSEIcopy);
        }
      }
    
      if (picInGOP ==0 && m_pcCfg->getSOPDescriptionSEIEnabled() ) // write SOP description SEI (if enabled) at the beginning of GOP
      {
        SEISOPDescription* sopDescriptionSEI = new SEISOPDescription();
        m_seiEncoder.initSEISOPDescription(sopDescriptionSEI, slice, picInGOP, m_iLastIDR, m_iGopSize);
        seiMessages.push_back(sopDescriptionSEI);
      }
    
      if( ( m_pcEncLib->getRecoveryPointSEIEnabled() ) && ( slice->getSliceType() == I_SLICE ) )
      {
        if( m_pcEncLib->getGradualDecodingRefreshInfoEnabled() && !slice->getRapPicFlag() )
        {
          // Gradual decoding refresh SEI
          SEIGradualDecodingRefreshInfo *gradualDecodingRefreshInfoSEI = new SEIGradualDecodingRefreshInfo();
          gradualDecodingRefreshInfoSEI->m_gdrForegroundFlag = true; // Indicating all "foreground"
          seiMessages.push_back(gradualDecodingRefreshInfoSEI);
        }
        // Recovery point SEI
        SEIRecoveryPoint *recoveryPointSEI = new SEIRecoveryPoint();
        m_seiEncoder.initSEIRecoveryPoint(recoveryPointSEI, slice);
        seiMessages.push_back(recoveryPointSEI);
      }
      if (m_pcCfg->getTemporalLevel0IndexSEIEnabled())
      {
        SEITemporalLevel0Index *temporalLevel0IndexSEI = new SEITemporalLevel0Index();
        m_seiEncoder.initTemporalLevel0IndexSEI(temporalLevel0IndexSEI, slice);
        seiMessages.push_back(temporalLevel0IndexSEI);
      }
    
      if( m_pcEncLib->getNoDisplaySEITLayer() && ( slice->getTLayer() >= m_pcEncLib->getNoDisplaySEITLayer() ) )
      {
        SEINoDisplay *seiNoDisplay = new SEINoDisplay;
        seiNoDisplay->m_noDisplay = true;
        seiMessages.push_back(seiNoDisplay);
      }
    
      // insert one Colour Remapping Info SEI for the picture (if the file exists)
      if (!m_pcCfg->getColourRemapInfoSEIFileRoot().empty())
      {
        SEIColourRemappingInfo *seiColourRemappingInfo = new SEIColourRemappingInfo();
        const bool success = m_seiEncoder.initSEIColourRemappingInfo(seiColourRemappingInfo, slice->getPOC() );
    
        if(success)
        {
          seiMessages.push_back(seiColourRemappingInfo);
        }
        else
        {
          delete seiColourRemappingInfo;
        }
      }
    }
    
    void EncGOP::xCreateScalableNestingSEI (SEIMessages& seiMessages, SEIMessages& nestedSeiMessages)
    {
      SEIMessages tmpMessages;
      while (!nestedSeiMessages.empty())
      {
        SEI* sei=nestedSeiMessages.front();
        nestedSeiMessages.pop_front();
        tmpMessages.push_back(sei);
        SEIScalableNesting *nestingSEI = new SEIScalableNesting();
        m_seiEncoder.initSEIScalableNesting(nestingSEI, tmpMessages);
        seiMessages.push_back(nestingSEI);
        tmpMessages.clear();
      }
    }
    
    void EncGOP::xCreatePictureTimingSEI  (int IRAPGOPid, SEIMessages& seiMessages, SEIMessages& nestedSeiMessages, SEIMessages& duInfoSeiMessages, Slice *slice, bool isField, std::deque<DUData> &duData)
    {
    
    Virginie Drugeon's avatar
    Virginie Drugeon committed
      const HRDParameters *hrd = slice->getSPS()->getHrdParameters();
    
    
      // update decoding unit parameters
      if( ( m_pcCfg->getPictureTimingSEIEnabled() || m_pcCfg->getDecodingUnitInfoSEIEnabled() ) &&
        ( slice->getSPS()->getVuiParametersPresentFlag() ) &&
        (  hrd->getNalHrdParametersPresentFlag() || hrd->getVclHrdParametersPresentFlag() ) )
      {
        int picSptDpbOutputDuDelay = 0;
        SEIPictureTiming *pictureTimingSEI = new SEIPictureTiming();
    
        // DU parameters
        if( hrd->getSubPicCpbParamsPresentFlag() )
        {
          uint32_t numDU = (uint32_t) duData.size();
          pictureTimingSEI->m_numDecodingUnitsMinus1     = ( numDU - 1 );
          pictureTimingSEI->m_duCommonCpbRemovalDelayFlag = false;
          pictureTimingSEI->m_numNalusInDuMinus1.resize( numDU );
          pictureTimingSEI->m_duCpbRemovalDelayMinus1.resize( numDU );
        }
        pictureTimingSEI->m_auCpbRemovalDelay = std::min<int>(std::max<int>(1, m_totalCoded - m_lastBPSEI), static_cast<int>(pow(2, static_cast<double>(hrd->getCpbRemovalDelayLengthMinus1()+1)))); // Syntax element signalled as minus, hence the .
        pictureTimingSEI->m_picDpbOutputDelay = slice->getSPS()->getNumReorderPics(slice->getSPS()->getMaxTLayers()-1) + slice->getPOC() - m_totalCoded;
        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)
        {
          m_lastBPSEI = m_totalCoded;
        }
    
    
        if( m_pcCfg->getPictureTimingSEIEnabled() )
        {
          pictureTimingSEI->m_picStruct = (isField && slice->getPic()->topField)? 1 : isField? 2 : 0;
          seiMessages.push_back(pictureTimingSEI);
    
          if ( m_pcCfg->getScalableNestingSEIEnabled() ) // put picture timing SEI into scalable nesting SEI
          {
            SEIPictureTiming *pictureTimingSEIcopy = new SEIPictureTiming();
            pictureTimingSEI->copyTo(*pictureTimingSEIcopy);
            nestedSeiMessages.push_back(pictureTimingSEIcopy);
          }
        }
    
        if( m_pcCfg->getDecodingUnitInfoSEIEnabled() && hrd->getSubPicCpbParamsPresentFlag() )
        {
          for( int i = 0; i < ( pictureTimingSEI->m_numDecodingUnitsMinus1 + 1 ); i ++ )
          {
            SEIDecodingUnitInfo *duInfoSEI = new SEIDecodingUnitInfo();
            duInfoSEI->m_decodingUnitIdx = i;
            duInfoSEI->m_duSptCpbRemovalDelay = pictureTimingSEI->m_duCpbRemovalDelayMinus1[i] + 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;
      }
    
    Virginie Drugeon's avatar
    Virginie Drugeon committed
      const HRDParameters *hrd = sps->getHrdParameters();
    
      if( hrd->getSubPicCpbParamsPresentFlag() )
      {
        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;
    
        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 ] = 0; /* don't care */
        }
        else
        {
          rDuCpbRemovalDelayMinus1[ numDU - 1 ] = 0;/* by definition */
          uint32_t tmp = 0;
          uint32_t accum = 0;
    
          for( i = ( numDU - 2 ); i >= 0; i -- )
          {
    
    Virginie Drugeon's avatar
    Virginie Drugeon committed
            ui64Tmp = ( ( ( duData[numDU - 1].accumBitsDU  - duData[i].accumBitsDU ) * ( sps->getTimingInfo()->getTimeScale() / sps->getTimingInfo()->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;
    
    Virginie Drugeon's avatar
    Virginie Drugeon committed
            ui64Tmp = ( ( ( duData[numDU - 1].accumBitsDU  - duData[i].accumBitsDU ) * ( sps->getTimingInfo()->getTimeScale() / sps->getTimingInfo()->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 ] = (uint32_t)ui64Tmp - uiPrev - 1;
            if( (int)rDuCpbRemovalDelayMinus1[ i ] < 0 )
            {
              rDuCpbRemovalDelayMinus1[ i ] = 0;
            }
            else if (tmp > 0 && flag == 1)
            {
              tmp --;
            }
            accum += rDuCpbRemovalDelayMinus1[ i ] + 1;
            uiPrev = accum;
          }
        }
      }
    }
    void EncGOP::xUpdateDuInfoSEI(SEIMessages &duInfoSeiMessages, SEIPictureTiming *pictureTimingSEI)
    {
      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;
        duInfoSEI->m_duSptCpbRemovalDelay = pictureTimingSEI->m_duCpbRemovalDelayMinus1[i] + 1;
        duInfoSEI->m_dpbOutputDuDelayPresentFlag = false;
        i++;
      }
    }
    
    static void
    cabac_zero_word_padding(Slice *const pcSlice, Picture *const pcPic, const std::size_t binCountsInNalUnits, const std::size_t numBytesInVclNalUnits, std::ostringstream &nalUnitData, const bool cabacZeroWordPaddingEnabled)
    {
      const SPS &sps=*(pcSlice->getSPS());
      const ChromaFormat format = sps.getChromaFormatIdc();
      const int log2subWidthCxsubHeightC = (::getComponentScaleX(COMPONENT_Cb, format)+::getComponentScaleY(COMPONENT_Cb, format));
      const int minCuWidth  = pcPic->cs->pcv->minCUWidth;
      const int minCuHeight = pcPic->cs->pcv->minCUHeight;
      const int paddedWidth = ((sps.getPicWidthInLumaSamples()  + minCuWidth  - 1) / minCuWidth) * minCuWidth;
      const int paddedHeight= ((sps.getPicHeightInLumaSamples() + minCuHeight - 1) / minCuHeight) * minCuHeight;
      const int rawBits = paddedWidth * paddedHeight *
                             (sps.getBitDepth(CHANNEL_TYPE_LUMA) + 2*(sps.getBitDepth(CHANNEL_TYPE_CHROMA)>>log2subWidthCxsubHeightC));
      const std::size_t threshold = (32/3)*numBytesInVclNalUnits + (rawBits/32);
      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+31)/32;
    
        if (targetNumBytesInVclNalUnits>numBytesInVclNalUnits) // It should be!
        {
          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