Skip to content
Snippets Groups Projects
EncGOP.cpp 266 KiB
Newer Older
  • Learn to ignore specific revisions
  •               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;
      }
    
      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 -- )
          {
    
            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;
    
            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++);
    
        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++;
                  }
                }
              }
    
              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();
    
    #if U0132_TARGET_BITS_SATURATION
      if (m_pcRateCtrl->getCpbSaturationEnabled() && frameLevel != 0)
      {
        int estimatedCpbFullness = m_pcRateCtrl->getCpbState() + m_pcRateCtrl->getBufferingRate();
    
        // prevent overflow
        if (estimatedCpbFullness - estimatedBits > (int)(m_pcRateCtrl->getCpbSize()*0.9f))
        {
          estimatedBits = estimatedCpbFullness - (int)(m_pcRateCtrl->getCpbSize()*0.9f);
        }
    
        estimatedCpbFullness -= m_pcRateCtrl->getBufferingRate();
        // prevent underflow
    #if V0078_ADAPTIVE_LOWER_BOUND
        if (estimatedCpbFullness - estimatedBits < m_pcRateCtrl->getRCPic()->getLowerBound())
        {
          estimatedBits = std::max(200, estimatedCpbFullness - m_pcRateCtrl->getRCPic()->getLowerBound());
        }
    #else
        if (estimatedCpbFullness - estimatedBits < (int)(m_pcRateCtrl->getCpbSize()*0.1f))
        {
          estimatedBits = std::max(200, estimatedCpbFullness - (int)(m_pcRateCtrl->getCpbSize()*0.1f));
        }
    #endif
    
        m_pcRateCtrl->getRCPic()->setTargetBits(estimatedBits);
      }
    #endif
    
      int sliceQP = m_pcCfg->getInitialQP();
      if ( ( 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 );
    
    #if U0132_TARGET_BITS_SATURATION
          if (m_pcRateCtrl->getCpbSaturationEnabled() )
          {
            int estimatedCpbFullness = m_pcRateCtrl->getCpbState() + m_pcRateCtrl->getBufferingRate();
    
            // prevent overflow
            if (estimatedCpbFullness - bits > (int)(m_pcRateCtrl->getCpbSize()*0.9f))
            {
              bits = estimatedCpbFullness - (int)(m_pcRateCtrl->getCpbSize()*0.9f);
            }
    
            estimatedCpbFullness -= m_pcRateCtrl->getBufferingRate();
            // prevent underflow
    #if V0078_ADAPTIVE_LOWER_BOUND
            if (estimatedCpbFullness - bits < m_pcRateCtrl->getRCPic()->getLowerBound())
            {
              bits = estimatedCpbFullness - m_pcRateCtrl->getRCPic()->getLowerBound();
            }
    #else
            if (estimatedCpbFullness - bits < (int)(m_pcRateCtrl->getCpbSize()*0.1f))
            {
              bits = estimatedCpbFullness - (int)(m_pcRateCtrl->getCpbSize()*0.1f);
            }
    #endif
          }
    #endif
    
          if ( bits < 200 )
          {
            bits = 200;
          }
          m_pcRateCtrl->getRCPic()->setTargetBits( bits );
        }
    
        list<EncRCPic*> listPreviousPicture = m_pcRateCtrl->getPicList();
        m_pcRateCtrl->getRCPic()->getLCUInitTargetBits();
        lambda  = m_pcRateCtrl->getRCPic()->estimatePicLambda( listPreviousPicture, 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 );
      }
    
      sliceQP = Clip3( -slice->getSPS()->getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, sliceQP );
      m_pcRateCtrl->getRCPic()->setPicEstQP( sliceQP );
    
      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();
    
        m_pcReshaper->setSrcReshaped(false);
        m_pcReshaper->setRecReshaped(true);
    
        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!");
        }
    
        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!");
          }
    
          if (realSliceType != condSliceType)
          {
            m_pcReshaper->setCTUFlag(true);
          }
    
        }
        else
        {
          if (!m_pcReshaper->getReshapeFlag())
          {
            m_pcReshaper->setCTUFlag(false);
          }
          else
            m_pcReshaper->setCTUFlag(true);
    
          m_pcReshaper->getSliceReshaperInfo().setSliceReshapeModelPresentFlag(false);
    
          if (m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_PQ)
          {
            m_pcEncLib->getRdCost()->restoreReshapeLumaLevelToWeightTable();
          }
          else if (m_pcCfg->getReshapeSignalType() == RESHAPE_SIGNAL_SDR || 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!");
          }
        }
    
    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);