/* 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-2015, 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 TEncGOP.cpp \brief GOP encoder class */ #include #include #include #include "TEncTop.h" #include "TEncGOP.h" #include "TEncAnalyze.h" #include "libmd5/MD5.h" #include "TLibCommon/SEI.h" #include "TLibCommon/NAL.h" #include "NALwrite.h" #include #include #include using namespace std; //! \ingroup TLibEncoder //! \{ // ==================================================================================================================== // Constructor / destructor / initialization / destroy // ==================================================================================================================== Int getLSB(Int poc, Int maxLSB) { if (poc >= 0) { return poc % maxLSB; } else { return (maxLSB - ((-poc) % maxLSB)) % maxLSB; } } TEncGOP::TEncGOP() { m_iLastIDR = 0; m_iGopSize = 0; m_iNumPicCoded = 0; //Niko m_bFirst = true; m_iLastRecoveryPicPOC = 0; m_pcCfg = NULL; m_pcSliceEncoder = NULL; m_pcListPic = NULL; m_pcEntropyCoder = NULL; m_pcCavlcCoder = NULL; m_pcSbacCoder = NULL; m_pcBinCABAC = 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; #if NH_MV m_layerId = 0; m_viewId = 0; m_pocLastCoded = -1; #if NH_3D m_viewIndex = 0; m_isDepth = false; #endif #endif m_associatedIRAPType = NAL_UNIT_CODED_SLICE_IDR_N_LP; m_associatedIRAPPOC = 0; return; } TEncGOP::~TEncGOP() { } /** Create list to contain pointers to CTU start addresses of slice. */ Void TEncGOP::create() { m_bLongtermTestPictureHasBeenCoded = 0; m_bLongtermTestPictureHasBeenCoded2 = 0; } Void TEncGOP::destroy() { } Void TEncGOP::init ( TEncTop* pcTEncTop ) { m_pcEncTop = pcTEncTop; m_pcCfg = pcTEncTop; m_seiEncoder.init(m_pcCfg, pcTEncTop, this); m_pcSliceEncoder = pcTEncTop->getSliceEncoder(); m_pcListPic = pcTEncTop->getListPic(); m_pcEntropyCoder = pcTEncTop->getEntropyCoder(); m_pcCavlcCoder = pcTEncTop->getCavlcCoder(); m_pcSbacCoder = pcTEncTop->getSbacCoder(); m_pcBinCABAC = pcTEncTop->getBinCABAC(); m_pcLoopFilter = pcTEncTop->getLoopFilter(); m_pcSAO = pcTEncTop->getSAO(); m_pcRateCtrl = pcTEncTop->getRateCtrl(); m_lastBPSEI = 0; m_totalCoded = 0; #if NH_MV m_ivPicLists = pcTEncTop->getIvPicLists(); m_layerId = pcTEncTop->getLayerId(); m_viewId = pcTEncTop->getViewId(); #if NH_3D m_viewIndex = pcTEncTop->getViewIndex(); m_isDepth = pcTEncTop->getIsDepth(); #endif #endif #if NH_3D_IC m_aICEnableCandidate = pcTEncTop->getICEnableCandidate(); m_aICEnableNum = pcTEncTop->getICEnableNum(); #endif #if KWU_FIX_URQ m_pcRateCtrl = pcTEncTop->getRateCtrl(); #endif } Int TEncGOP::xWriteVPS (AccessUnit &accessUnit, const TComVPS *vps) { OutputNALUnit nalu(NAL_UNIT_VPS); m_pcEntropyCoder->setBitstream(&nalu.m_Bitstream); m_pcEntropyCoder->encodeVPS(vps); accessUnit.push_back(new NALUnitEBSP(nalu)); return (Int)(accessUnit.back()->m_nalUnitData.str().size()) * 8; } Int TEncGOP::xWriteSPS (AccessUnit &accessUnit, const TComSPS *sps) { #if NH_MV OutputNALUnit nalu(NAL_UNIT_SPS, 0, getLayerId() ); #else OutputNALUnit nalu(NAL_UNIT_SPS); #endif m_pcEntropyCoder->setBitstream(&nalu.m_Bitstream); m_pcEntropyCoder->encodeSPS(sps); accessUnit.push_back(new NALUnitEBSP(nalu)); return (Int)(accessUnit.back()->m_nalUnitData.str().size()) * 8; } Int TEncGOP::xWritePPS (AccessUnit &accessUnit, const TComPPS *pps) { #if NH_MV OutputNALUnit nalu(NAL_UNIT_PPS, 0, getLayerId() ); #else OutputNALUnit nalu(NAL_UNIT_PPS); #endif m_pcEntropyCoder->setBitstream(&nalu.m_Bitstream); m_pcEntropyCoder->encodePPS(pps); accessUnit.push_back(new NALUnitEBSP(nalu)); return (Int)(accessUnit.back()->m_nalUnitData.str().size()) * 8; } Int TEncGOP::xWriteParameterSets (AccessUnit &accessUnit, TComSlice *slice) { Int actualTotalBits = 0; #if NH_MV if ( getLayerId() == 0 ) { actualTotalBits += xWriteVPS(accessUnit, m_pcEncTop->getVPS()); } #else actualTotalBits += xWriteVPS(accessUnit, m_pcEncTop->getVPS()); #endif actualTotalBits += xWriteSPS(accessUnit, slice->getSPS()); actualTotalBits += xWritePPS(accessUnit, slice->getPPS()); return actualTotalBits; } // write SEI list into one NAL unit and add it to the Access unit at auPos Void TEncGOP::xWriteSEI (NalUnitType naluType, SEIMessages& seiMessages, AccessUnit &accessUnit, AccessUnit::iterator &auPos, Int temporalId, const TComSPS *sps) { // don't do anything, if we get an empty list if (seiMessages.empty()) { return; } #if NH_MV OutputNALUnit nalu(naluType, temporalId, getLayerId() ); #else OutputNALUnit nalu(naluType, temporalId); #endif m_seiWriter.writeSEImessages(nalu.m_Bitstream, seiMessages, sps, false); auPos = accessUnit.insert(auPos, new NALUnitEBSP(nalu)); auPos++; } Void TEncGOP::xWriteSEISeparately (NalUnitType naluType, SEIMessages& seiMessages, AccessUnit &accessUnit, AccessUnit::iterator &auPos, Int temporalId, const TComSPS *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); #if NH_MV OutputNALUnit nalu(naluType, temporalId, getLayerId() ); #else OutputNALUnit nalu(naluType, temporalId); #endif m_seiWriter.writeSEImessages(nalu.m_Bitstream, tmpMessages, sps, false); auPos = accessUnit.insert(auPos, new NALUnitEBSP(nalu)); auPos++; } } Void TEncGOP::xClearSEIs(SEIMessages& seiMessages, Bool deleteMessages) { if (deleteMessages) { deleteSEIs(seiMessages); } else { seiMessages.clear(); } } // write SEI messages as separate NAL units ordered Void TEncGOP::xWriteLeadingSEIOrdered (SEIMessages& seiMessages, SEIMessages& duInfoSeiMessages, AccessUnit &accessUnit, Int temporalId, const TComSPS *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_SPS || (*itNalu)->m_nalUnitType==NAL_UNIT_PPS )) { itNalu++; } SEIMessages localMessages = seiMessages; SEIMessages currentMessages; #if ENC_DEC_TRACE 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); assert (currentMessages.size() <= 1); 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); assert (currentMessages.size() <= 1); 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); assert (currentMessages.size() <= 1); 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 TEncGOP::xWriteLeadingSEIMessages (SEIMessages& seiMessages, SEIMessages& duInfoSeiMessages, AccessUnit &accessUnit, Int temporalId, const TComSPS *sps, std::deque &duData) { AccessUnit testAU; SEIMessages picTimingSEIs = getSeisByType(seiMessages, SEI::PICTURE_TIMING); assert (picTimingSEIs.size() < 2); 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 TEncGOP::xWriteTrailingSEIMessages (SEIMessages& seiMessages, AccessUnit &accessUnit, Int temporalId, const TComSPS *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 TEncGOP::xWriteDuSEIMessages (SEIMessages& duInfoSeiMessages, AccessUnit &accessUnit, Int temporalId, const TComSPS *sps, std::deque &duData) { const TComHRD *hrd = sps->getVuiParameters()->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 assert (false); 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 TEncGOP::xCreateIRAPLeadingSEIMessages (SEIMessages& seiMessages, const TComSPS *sps, const TComPPS *pps) { #if NH_MV OutputNALUnit nalu(NAL_UNIT_PREFIX_SEI, 0, getLayerId()); #else OutputNALUnit nalu(NAL_UNIT_PREFIX_SEI); #endif if(m_pcCfg->getActiveParameterSetsSEIEnabled()) { SEIActiveParameterSets *sei = new SEIActiveParameterSets; m_seiEncoder.initSEIActiveParameterSets (sei, m_pcCfg->getVPS(), 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 TComSEIMasteringDisplay &seiCfg=m_pcCfg->getMasteringDisplaySEI(); SEIMasteringDisplayColourVolume *sei = new SEIMasteringDisplayColourVolume; sei->values = seiCfg; seiMessages.push_back(sei); } #if NH_MV if( m_pcCfg->getSubBitstreamPropSEIEnabled() && ( getLayerId() == 0 ) ) { SEISubBitstreamProperty *sei = new SEISubBitstreamProperty; m_seiEncoder.initSEISubBitstreamProperty( sei, sps ); seiMessages.push_back(sei); } #endif } Void TEncGOP::xCreatePerPictureSEIMessages (Int picInGOP, SEIMessages& seiMessages, SEIMessages& nestedSeiMessages, TComSlice *slice) { if( ( m_pcCfg->getBufferingPeriodSEIEnabled() ) && ( slice->getSliceType() == I_SLICE ) && ( slice->getSPS()->getVuiParametersPresentFlag() ) && ( ( slice->getSPS()->getVuiParameters()->getHrdParameters()->getNalHrdParametersPresentFlag() ) || ( slice->getSPS()->getVuiParameters()->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_pcEncTop->getRecoveryPointSEIEnabled() ) && ( slice->getSliceType() == I_SLICE ) ) { if( m_pcEncTop->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(slice->getSPS()->getVuiParametersPresentFlag() && m_pcCfg->getChromaSamplingFilterHintEnabled() && ( slice->getSliceType() == I_SLICE )) { SEIChromaSamplingFilterHint *seiChromaSamplingFilterHint = new SEIChromaSamplingFilterHint; m_seiEncoder.initSEIChromaSamplingFilterHint(seiChromaSamplingFilterHint, m_pcCfg->getChromaSamplingHorFilterIdc(), m_pcCfg->getChromaSamplingVerFilterIdc()); seiMessages.push_back(seiChromaSamplingFilterHint); } if( m_pcEncTop->getNoDisplaySEITLayer() && ( slice->getTLayer() >= m_pcEncTop->getNoDisplaySEITLayer() ) ) { SEINoDisplay *seiNoDisplay = new SEINoDisplay; seiNoDisplay->m_noDisplay = true; seiMessages.push_back(seiNoDisplay); } } Void TEncGOP::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 TEncGOP::xCreatePictureTimingSEI (Int IRAPGOPid, SEIMessages& seiMessages, SEIMessages& nestedSeiMessages, SEIMessages& duInfoSeiMessages, TComSlice *slice, Bool isField, std::deque &duData) { Int picSptDpbOutputDuDelay = 0; #if !NH_MV SEIPictureTiming *pictureTimingSEI = new SEIPictureTiming(); #endif const TComVUI *vui = slice->getSPS()->getVuiParameters(); const TComHRD *hrd = vui->getHrdParameters(); // update decoding unit parameters if( ( m_pcCfg->getPictureTimingSEIEnabled() || m_pcCfg->getDecodingUnitInfoSEIEnabled() ) && ( slice->getSPS()->getVuiParametersPresentFlag() ) && ( hrd->getNalHrdParametersPresentFlag() || hrd->getVclHrdParametersPresentFlag() ) ) { #if NH_MV // Preliminary fix to avoid memory leak. SEIPictureTiming *pictureTimingSEI = new SEIPictureTiming(); #endif // DU parameters if( hrd->getSubPicCpbParamsPresentFlag() ) { UInt numDU = (UInt) 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(std::max(1, m_totalCoded - m_lastBPSEI), static_cast(pow(2, static_cast(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( hrd->getSubPicCpbParamsPresentFlag() ) { Int i; UInt64 ui64Tmp; UInt uiPrev = 0; UInt numDU = ( pictureTimingSEI->m_numDecodingUnitsMinus1 + 1 ); std::vector &rDuCpbRemovalDelayMinus1 = pictureTimingSEI->m_duCpbRemovalDelayMinus1; UInt 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 */ UInt tmp = 0; UInt accum = 0; for( i = ( numDU - 2 ); i >= 0; i -- ) { ui64Tmp = ( ( ( duData[numDU - 1].accumBitsDU - duData[i].accumBitsDU ) * ( vui->getTimingInfo()->getTimeScale() / vui->getTimingInfo()->getNumUnitsInTick() ) * ( hrd->getTickDivisorMinus2() + 2 ) ) / ( m_pcCfg->getTargetBitrate() ) ); if( (UInt)ui64Tmp > maxDiff ) { tmp ++; } } uiPrev = 0; UInt flag = 0; for( i = ( numDU - 2 ); i >= 0; i -- ) { flag = 0; ui64Tmp = ( ( ( duData[numDU - 1].accumBitsDU - duData[i].accumBitsDU ) * ( vui->getTimingInfo()->getTimeScale() / vui->getTimingInfo()->getNumUnitsInTick() ) * ( hrd->getTickDivisorMinus2() + 2 ) ) / ( m_pcCfg->getTargetBitrate() ) ); if( (UInt)ui64Tmp > maxDiff ) { if(uiPrev >= maxDiff - tmp) { ui64Tmp = uiPrev + 1; flag = 1; } else ui64Tmp = maxDiff - tmp + 1; } rDuCpbRemovalDelayMinus1[ i ] = (UInt)ui64Tmp - uiPrev - 1; if( (Int)rDuCpbRemovalDelayMinus1[ i ] < 0 ) { rDuCpbRemovalDelayMinus1[ i ] = 0; } else if (tmp > 0 && flag == 1) { tmp --; } accum += rDuCpbRemovalDelayMinus1[ i ] + 1; uiPrev = accum; } } } if( m_pcCfg->getPictureTimingSEIEnabled() ) { pictureTimingSEI->m_picStruct = (isField && slice->getPic()->isTopField())? 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); } } } } Void TEncGOP::xUpdateDuData(AccessUnit &testAU, std::deque &duData) { if (duData.empty()) { return; } // fix first UInt numNalUnits = (UInt)testAU.size(); UInt numRBSPBytes = 0; for (AccessUnit::const_iterator it = testAU.begin(); it != testAU.end(); it++) { numRBSPBytes += UInt((*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; igetDecodingUnitInfoSEIEnabled()) { 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->getDecodedPictureHashSEIEnabled()) { duData.back().accumBitsDU += ( 20 << 3 ); // probably around 20 bytes - should be further adjusted, e.g. by type duData.back().accumNalsDU += 1; } } Void TEncGOP::xUpdateTimingSEI(SEIPictureTiming *pictureTimingSEI, std::deque &duData, const TComSPS *sps) { if (!pictureTimingSEI) { return; } const TComVUI *vui = sps->getVuiParameters(); const TComHRD *hrd = vui->getHrdParameters(); if( hrd->getSubPicCpbParamsPresentFlag() ) { Int i; UInt64 ui64Tmp; UInt uiPrev = 0; UInt numDU = ( pictureTimingSEI->m_numDecodingUnitsMinus1 + 1 ); std::vector &rDuCpbRemovalDelayMinus1 = pictureTimingSEI->m_duCpbRemovalDelayMinus1; UInt 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 */ UInt tmp = 0; UInt accum = 0; for( i = ( numDU - 2 ); i >= 0; i -- ) { ui64Tmp = ( ( ( duData[numDU - 1].accumBitsDU - duData[i].accumBitsDU ) * ( vui->getTimingInfo()->getTimeScale() / vui->getTimingInfo()->getNumUnitsInTick() ) * ( hrd->getTickDivisorMinus2() + 2 ) ) / ( m_pcCfg->getTargetBitrate() ) ); if( (UInt)ui64Tmp > maxDiff ) { tmp ++; } } uiPrev = 0; UInt flag = 0; for( i = ( numDU - 2 ); i >= 0; i -- ) { flag = 0; ui64Tmp = ( ( ( duData[numDU - 1].accumBitsDU - duData[i].accumBitsDU ) * ( vui->getTimingInfo()->getTimeScale() / vui->getTimingInfo()->getNumUnitsInTick() ) * ( hrd->getTickDivisorMinus2() + 2 ) ) / ( m_pcCfg->getTargetBitrate() ) ); if( (UInt)ui64Tmp > maxDiff ) { if(uiPrev >= maxDiff - tmp) { ui64Tmp = uiPrev + 1; flag = 1; } else ui64Tmp = maxDiff - tmp + 1; } rDuCpbRemovalDelayMinus1[ i ] = (UInt)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 TEncGOP::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(TComSlice *const pcSlice, TComPic *const pcPic, const std::size_t binCountsInNalUnits, const std::size_t numBytesInVclNalUnits, std::ostringstream &nalUnitData, const Bool cabacZeroWordPaddingEnabled) { const TComSPS &sps=*(pcSlice->getSPS()); const Int log2subWidthCxsubHeightC = (pcPic->getComponentScaleX(COMPONENT_Cb)+pcPic->getComponentScaleY(COMPONENT_Cb)); const Int minCuWidth = pcPic->getMinCUWidth(); const Int minCuHeight = pcPic->getMinCUHeight(); 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 zeroBytesPadding(numberOfAdditionalCabacZeroBytes, Char(0)); for(std::size_t i=0; igetGOPEntry(iGOPid).m_POC - isField; } // check if POC corresponds to IRAP NalUnitType tmpUnitType = pEncGop->getNalUnitType(pocCurr, lastIDR, isField); if(tmpUnitType >= NAL_UNIT_CODED_SLICE_BLA_W_LP && 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 UInt calculateCollocatedFromL1Flag(TEncCfg *pCfg, const Int GOPid, const Int gopSize) { Int iCloseLeft=1, iCloseRight=-1; for(Int i = 0; igetGOPEntry(GOPid).m_numRefPics; i++) { Int iRef = pCfg->getGOPEntry(GOPid).m_referencePics[i]; if(iRef>0&&(iRefiCloseLeft||iCloseLeft==1)) { iCloseLeft=iRef; } } if(iCloseRight>-1) { iCloseRight=iCloseRight+pCfg->getGOPEntry(GOPid).m_POC-1; } if(iCloseLeft<1) { iCloseLeft=iCloseLeft+pCfg->getGOPEntry(GOPid).m_POC-1; while(iCloseLeft<0) { iCloseLeft+=gopSize; } } Int iLeftQP=0, iRightQP=0; for(Int i=0; igetGOPEntry(i).m_POC==(iCloseLeft%gopSize)+1) { iLeftQP= pCfg->getGOPEntry(i).m_QPOffset; } if (pCfg->getGOPEntry(i).m_POC==(iCloseRight%gopSize)+1) { iRightQP=pCfg->getGOPEntry(i).m_QPOffset; } } if(iCloseRight>-1&&iRightQP& rcListPic, TComList& rcListPicYuvRecOut, std::list& accessUnitsInGOP) { xInitGOP( iPOCLast, iNumPicRcvd, false ); m_iNumPicCoded = 0; } #endif #if NH_MV Void TEncGOP::compressPicInGOP( Int iPOCLast, Int iNumPicRcvd, TComList& rcListPic, TComList& rcListPicYuvRecOut, std::list& accessUnitsInGOP, Bool isField, Bool isTff, const InputColourSpaceConversion snr_conversion, const Bool printFrameMSE, Int iGOPid ) #else Void TEncGOP::compressGOP( Int iPOCLast, Int iNumPicRcvd, TComList& rcListPic, TComList& rcListPicYuvRecOut, std::list& accessUnitsInGOP, Bool isField, Bool isTff, const InputColourSpaceConversion snr_conversion, const Bool printFrameMSE ) #endif { // TODO: Split this function up. TComPic* pcPic = NULL; TComPicYuv* pcPicYuvRecOut; TComSlice* pcSlice; TComOutputBitstream *pcBitstreamRedirect; pcBitstreamRedirect = new TComOutputBitstream; AccessUnit::iterator itLocationToPushSliceHeaderNALU; // used to store location where NALU containing slice header is to be inserted #if !NH_MV xInitGOP( iPOCLast, iNumPicRcvd, isField ); #endif m_iNumPicCoded = 0; SEIMessages leadingSeiMessages; SEIMessages nestedSeiMessages; SEIMessages duInfoSeiMessages; SEIMessages trailingSeiMessages; std::deque duData; SEIDecodingUnitInfo decodingUnitInfoSEI; EfficientFieldIRAPMapping effFieldIRAPMap; if (m_pcCfg->getEfficientFieldIRAPEnabled()) { effFieldIRAPMap.initialize(isField, m_iGopSize, iPOCLast, iNumPicRcvd, m_iLastIDR, this, m_pcCfg); } // reset flag indicating whether pictures have been encoded #if !NH_MV for ( Int iGOPid=0; iGOPid < m_iGopSize; iGOPid++ ) #endif { m_pcCfg->setEncodedFlag(iGOPid, false); } #if !NH_MV for ( Int iGOPid=0; iGOPid < m_iGopSize; iGOPid++ ) #endif { if (m_pcCfg->getEfficientFieldIRAPEnabled()) { iGOPid=effFieldIRAPMap.adjustGOPid(iGOPid); } //-- For time output for each slice clock_t iBeforeTime = clock(); UInt uiColDir = calculateCollocatedFromL1Flag(m_pcCfg, iGOPid, m_iGopSize); /////////////////////////////////////////////////////////////////////////////////////////////////// Initial to start encoding Int iTimeOffset; Int pocCurr; if(iPOCLast == 0) //case first frame or first top field { pocCurr=0; iTimeOffset = 1; } 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; iTimeOffset = 1; } else { pocCurr = iPOCLast - iNumPicRcvd + m_pcCfg->getGOPEntry(iGOPid).m_POC - ((isField && m_iGopSize>1) ? 1:0); iTimeOffset = m_pcCfg->getGOPEntry(iGOPid).m_POC; } if(pocCurr>=m_pcCfg->getFramesToBeEncoded()) { if (m_pcCfg->getEfficientFieldIRAPEnabled()) { iGOPid=effFieldIRAPMap.restoreGOPid(iGOPid); } #if NH_MV delete pcBitstreamRedirect; return; #else continue; #endif } 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 accessUnitsInGOP.push_back(AccessUnit()); AccessUnit& accessUnit = accessUnitsInGOP.back(); xGetBuffer( rcListPic, rcListPicYuvRecOut, iNumPicRcvd, iTimeOffset, pcPic, pcPicYuvRecOut, pocCurr, isField ); // Slice data initialization pcPic->clearSliceBuffer(); pcPic->allocateNewSlice(); m_pcSliceEncoder->setSliceIdx(0); pcPic->setCurrSliceIdx(0); #if NH_MV m_pcSliceEncoder->initEncSlice ( pcPic, iPOCLast, pocCurr, iGOPid, pcSlice, m_pcEncTop->getVPS(), getLayerId(), isField ); #else m_pcSliceEncoder->initEncSlice ( pcPic, iPOCLast, pocCurr, iGOPid, pcSlice, isField ); #endif //Set Frame/Field coding pcSlice->getPic()->setField(isField); pcSlice->setLastIDR(m_iLastIDR); pcSlice->setSliceIdx(0); #if NH_MV pcSlice->setRefPicSetInterLayer ( &m_refPicSetInterLayer0, &m_refPicSetInterLayer1 ); pcPic ->setLayerId ( getLayerId() ); pcPic ->setViewId ( getViewId() ); #if !NH_3D pcSlice->setLayerId ( getLayerId() ); pcSlice->setViewId ( getViewId() ); pcSlice->setVPS ( m_pcEncTop->getVPS() ); #else pcPic ->setViewIndex ( getViewIndex() ); pcPic ->setIsDepth( getIsDepth() ); pcSlice->setCamparaSlice( pcPic->getCodedScale(), pcPic->getCodedOffset() ); #endif #endif //set default slice level flag to the same as SPS level flag pcSlice->setLFCrossSliceBoundaryFlag( pcSlice->getPPS()->getLoopFilterAcrossSlicesEnabledFlag() ); #if NH_MV // Set the nal unit type pcSlice->setNalUnitType(getNalUnitType(pocCurr, m_iLastIDR, isField)); if( pcSlice->getSliceType() == B_SLICE ) { if( m_pcCfg->getGOPEntry( ( pcSlice->getRapPicFlag() && getLayerId() > 0 ) ? MAX_GOP : iGOPid ).m_sliceType == 'P' ) { pcSlice->setSliceType( P_SLICE ); } } // To be checked! if( pcSlice->getSliceType() == B_SLICE ) { if( m_pcCfg->getGOPEntry( ( pcSlice->getRapPicFlag() && getLayerId() > 0 ) ? MAX_GOP : iGOPid ).m_sliceType == 'I' ) { pcSlice->setSliceType( I_SLICE ); } } #else if(pcSlice->getSliceType()==B_SLICE&&m_pcCfg->getGOPEntry(iGOPid).m_sliceType=='P') { pcSlice->setSliceType(P_SLICE); } if(pcSlice->getSliceType()==B_SLICE&&m_pcCfg->getGOPEntry(iGOPid).m_sliceType=='I') { pcSlice->setSliceType(I_SLICE); } // Set the nal unit type pcSlice->setNalUnitType(getNalUnitType(pocCurr, m_iLastIDR, isField)); #endif if(pcSlice->getTemporalLayerNonReferenceFlag()) { if (pcSlice->getNalUnitType() == NAL_UNIT_CODED_SLICE_TRAIL_R && !(m_iGopSize == 1 && pcSlice->getSliceType() == I_SLICE)) // Add this condition to avoid POC issues with encoder_intra_main.cfg configuration (see #1127 in bug tracker) { pcSlice->setNalUnitType(NAL_UNIT_CODED_SLICE_TRAIL_N); } if(pcSlice->getNalUnitType()==NAL_UNIT_CODED_SLICE_RADL_R) { pcSlice->setNalUnitType(NAL_UNIT_CODED_SLICE_RADL_N); } if(pcSlice->getNalUnitType()==NAL_UNIT_CODED_SLICE_RASL_R) { pcSlice->setNalUnitType(NAL_UNIT_CODED_SLICE_RASL_N); } } if (m_pcCfg->getEfficientFieldIRAPEnabled()) { if ( pcSlice->getNalUnitType() == NAL_UNIT_CODED_SLICE_BLA_W_LP || pcSlice->getNalUnitType() == NAL_UNIT_CODED_SLICE_BLA_W_RADL || pcSlice->getNalUnitType() == NAL_UNIT_CODED_SLICE_BLA_N_LP || pcSlice->getNalUnitType() == NAL_UNIT_CODED_SLICE_IDR_W_RADL || pcSlice->getNalUnitType() == NAL_UNIT_CODED_SLICE_IDR_N_LP || pcSlice->getNalUnitType() == NAL_UNIT_CODED_SLICE_CRA ) // IRAP picture { m_associatedIRAPType = pcSlice->getNalUnitType(); m_associatedIRAPPOC = pocCurr; } pcSlice->setAssociatedIRAPType(m_associatedIRAPType); pcSlice->setAssociatedIRAPPOC(m_associatedIRAPPOC); } // Do decoding refresh marking if any pcSlice->decodingRefreshMarking(m_pocCRA, m_bRefreshPending, rcListPic, m_pcCfg->getEfficientFieldIRAPEnabled()); m_pcEncTop->selectReferencePictureSet(pcSlice, pocCurr, iGOPid); if (!m_pcCfg->getEfficientFieldIRAPEnabled()) { if ( pcSlice->getNalUnitType() == NAL_UNIT_CODED_SLICE_BLA_W_LP || pcSlice->getNalUnitType() == NAL_UNIT_CODED_SLICE_BLA_W_RADL || pcSlice->getNalUnitType() == NAL_UNIT_CODED_SLICE_BLA_N_LP || pcSlice->getNalUnitType() == NAL_UNIT_CODED_SLICE_IDR_W_RADL || pcSlice->getNalUnitType() == NAL_UNIT_CODED_SLICE_IDR_N_LP || pcSlice->getNalUnitType() == NAL_UNIT_CODED_SLICE_CRA ) // IRAP picture { m_associatedIRAPType = pcSlice->getNalUnitType(); m_associatedIRAPPOC = pocCurr; } pcSlice->setAssociatedIRAPType(m_associatedIRAPType); pcSlice->setAssociatedIRAPPOC(m_associatedIRAPPOC); } if ((pcSlice->checkThatAllRefPicsAreAvailable(rcListPic, pcSlice->getRPS(), false, m_iLastRecoveryPicPOC, m_pcCfg->getDecodingRefreshType() == 3) != 0) || (pcSlice->isIRAP()) || (m_pcCfg->getEfficientFieldIRAPEnabled() && isField && pcSlice->getAssociatedIRAPType() >= NAL_UNIT_CODED_SLICE_BLA_W_LP && pcSlice->getAssociatedIRAPType() <= NAL_UNIT_CODED_SLICE_CRA && pcSlice->getAssociatedIRAPPOC() == pcSlice->getPOC()+1) ) { pcSlice->createExplicitReferencePictureSetFromReference(rcListPic, pcSlice->getRPS(), pcSlice->isIRAP(), m_iLastRecoveryPicPOC, m_pcCfg->getDecodingRefreshType() == 3, m_pcCfg->getEfficientFieldIRAPEnabled()); } pcSlice->applyReferencePictureSet(rcListPic, pcSlice->getRPS()); if(pcSlice->getTLayer() > 0 && !( pcSlice->getNalUnitType() == NAL_UNIT_CODED_SLICE_RADL_N // Check if not a leading picture || pcSlice->getNalUnitType() == NAL_UNIT_CODED_SLICE_RADL_R || pcSlice->getNalUnitType() == NAL_UNIT_CODED_SLICE_RASL_N || pcSlice->getNalUnitType() == NAL_UNIT_CODED_SLICE_RASL_R ) ) { if(pcSlice->isTemporalLayerSwitchingPoint(rcListPic) || pcSlice->getSPS()->getTemporalIdNestingFlag()) { if(pcSlice->getTemporalLayerNonReferenceFlag()) { pcSlice->setNalUnitType(NAL_UNIT_CODED_SLICE_TSA_N); } else { pcSlice->setNalUnitType(NAL_UNIT_CODED_SLICE_TSA_R); } } else if(pcSlice->isStepwiseTemporalLayerSwitchingPointCandidate(rcListPic)) { Bool isSTSA=true; for(Int ii=iGOPid+1;(iigetGOPSize() && isSTSA==true);ii++) { Int lTid= m_pcCfg->getGOPEntry(ii).m_temporalId; if(lTid==pcSlice->getTLayer()) { const TComReferencePictureSet* nRPS = pcSlice->getSPS()->getRPSList()->getReferencePictureSet(ii); for(Int jj=0;jjgetNumberOfPictures();jj++) { if(nRPS->getUsed(jj)) { Int tPoc=m_pcCfg->getGOPEntry(ii).m_POC+nRPS->getDeltaPOC(jj); Int kk=0; for(kk=0;kkgetGOPSize();kk++) { if(m_pcCfg->getGOPEntry(kk).m_POC==tPoc) { break; } } Int tTid=m_pcCfg->getGOPEntry(kk).m_temporalId; if(tTid >= pcSlice->getTLayer()) { isSTSA=false; break; } } } } } if(isSTSA==true) { if(pcSlice->getTemporalLayerNonReferenceFlag()) { pcSlice->setNalUnitType(NAL_UNIT_CODED_SLICE_STSA_N); } else { pcSlice->setNalUnitType(NAL_UNIT_CODED_SLICE_STSA_R); } } } } arrangeLongtermPicturesInRPS(pcSlice, rcListPic); TComRefPicListModification* refPicListModification = pcSlice->getRefPicListModification(); refPicListModification->setRefPicListModificationFlagL0(0); refPicListModification->setRefPicListModificationFlagL1(0); #if NH_MV if ( pcSlice->getPPS()->getNumExtraSliceHeaderBits() > 0 ) { // Some more sophisticated algorithm to determine discardable_flag might be added here. pcSlice->setDiscardableFlag ( false ); } const TComVPS* vps = pcSlice->getVPS(); #if NH_3D Int numDirectRefLayers = vps ->getNumRefListLayers( getLayerId() ); #else Int numDirectRefLayers = vps ->getNumDirectRefLayers( getLayerId() ); #endif #if NH_3D pcSlice->setIvPicLists( m_ivPicLists ); Int gopNum = (pcSlice->getRapPicFlag() && getLayerId() > 0) ? MAX_GOP : iGOPid; GOPEntry gopEntry = m_pcCfg->getGOPEntry( gopNum ); #else GOPEntry gopEntry = m_pcCfg->getGOPEntry( (pcSlice->getRapPicFlag() && getLayerId() > 0) ? MAX_GOP : iGOPid ); #endif Bool interLayerPredLayerIdcPresentFlag = false; if ( getLayerId() > 0 && !vps->getAllRefLayersActiveFlag() && numDirectRefLayers > 0 ) { pcSlice->setInterLayerPredEnabledFlag ( gopEntry.m_numActiveRefLayerPics > 0 ); if ( pcSlice->getInterLayerPredEnabledFlag() && numDirectRefLayers > 1 ) { if ( !vps->getMaxOneActiveRefLayerFlag() ) { pcSlice->setNumInterLayerRefPicsMinus1( gopEntry.m_numActiveRefLayerPics - 1 ); } #if NH_3D if ( gopEntry.m_numActiveRefLayerPics != vps->getNumRefListLayers( getLayerId() ) ) #else if ( gopEntry.m_numActiveRefLayerPics != vps->getNumDirectRefLayers( getLayerId() ) ) #endif { interLayerPredLayerIdcPresentFlag = true; for (Int i = 0; i < gopEntry.m_numActiveRefLayerPics; i++ ) { pcSlice->setInterLayerPredLayerIdc( i, gopEntry.m_interLayerPredLayerIdc[ i ] ); } } } } if ( !interLayerPredLayerIdcPresentFlag ) { for( Int i = 0; i < pcSlice->getNumActiveRefLayerPics(); i++ ) { pcSlice->setInterLayerPredLayerIdc(i, pcSlice->getRefLayerPicIdc( i ) ); } } assert( pcSlice->getNumActiveRefLayerPics() == gopEntry.m_numActiveRefLayerPics ); #if NH_3D if ( m_pcEncTop->decProcAnnexI() ) { pcSlice->deriveInCmpPredAndCpAvailFlag( ); if ( pcSlice->getInCmpPredAvailFlag() ) { pcSlice->setInCompPredFlag( gopEntry.m_interCompPredFlag ); } else { if (gopEntry.m_interCompPredFlag ) { if ( gopNum == MAX_GOP) { printf( "\nError: FrameI_l%d cannot enable inter-component prediction on slice level. All reference layers need to be available and at least one tool using inter-component prediction must be enabled in the SPS. \n", pcSlice->getVPS()->getLayerIdInVps( getLayerId() ) ); } else { printf( "\nError: Frame%d_l%d cannot enable inter-component prediction on slice level. All reference layers need to be available and at least one tool using inter-component prediction must be enabled in the SPS. \n", gopNum, pcSlice->getVPS()->getLayerIdInVps( getLayerId() ) ); } exit(EXIT_FAILURE); } } pcSlice->init3dToolParameters(); pcSlice->checkInCompPredRefLayers(); } #if NH_3D_IV_MERGE // This needs to be done after initialization of 3D tool parameters. pcSlice->setMaxNumMergeCand ( m_pcCfg->getMaxNumMergeCand() + ( ( pcSlice->getMpiFlag( ) || pcSlice->getIvMvPredFlag( ) || pcSlice->getViewSynthesisPredFlag( ) ) ? 1 : 0 )); #endif #endif pcSlice->createInterLayerReferencePictureSet( m_ivPicLists, m_refPicSetInterLayer0, m_refPicSetInterLayer1 ); pcSlice->setNumRefIdx(REF_PIC_LIST_0,min(gopEntry.m_numRefPicsActive,( pcSlice->getRPS()->getNumberOfPictures() + (Int) m_refPicSetInterLayer0.size() + (Int) m_refPicSetInterLayer1.size()) ) ); pcSlice->setNumRefIdx(REF_PIC_LIST_1,min(gopEntry.m_numRefPicsActive,( pcSlice->getRPS()->getNumberOfPictures() + (Int) m_refPicSetInterLayer0.size() + (Int) m_refPicSetInterLayer1.size()) ) ); std::vector< TComPic* > tempRefPicLists[2]; std::vector< Bool > usedAsLongTerm [2]; Int numPocTotalCurr; pcSlice->getTempRefPicLists( rcListPic, m_refPicSetInterLayer0, m_refPicSetInterLayer1, tempRefPicLists, usedAsLongTerm, numPocTotalCurr, true ); xSetRefPicListModificationsMv( tempRefPicLists, pcSlice, iGOPid ); #else pcSlice->setNumRefIdx(REF_PIC_LIST_0,min(m_pcCfg->getGOPEntry(iGOPid).m_numRefPicsActive,pcSlice->getRPS()->getNumberOfPictures())); pcSlice->setNumRefIdx(REF_PIC_LIST_1,min(m_pcCfg->getGOPEntry(iGOPid).m_numRefPicsActive,pcSlice->getRPS()->getNumberOfPictures())); #endif // Set reference list #if NH_MV pcSlice->setRefPicList( tempRefPicLists, usedAsLongTerm, numPocTotalCurr ); #else pcSlice->setRefPicList ( rcListPic ); #endif #if NH_3D_NBDV pcSlice->setDefaultRefView(); #endif #if NH_3D_ARP //GT: This seems to be broken when layerId in vps is not equal to layerId in nuh pcSlice->setARPStepNum(m_ivPicLists); #endif #if NH_3D_IC pcSlice->setICEnableCandidate( m_aICEnableCandidate ); pcSlice->setICEnableNum( m_aICEnableNum ); #endif // Slice info. refinement #if NH_MV if ( pcSlice->getSliceType() == B_SLICE ) { if( m_pcCfg->getGOPEntry( ( pcSlice->getRapPicFlag() == true && getLayerId() > 0 ) ? MAX_GOP : iGOPid ).m_sliceType == 'P' ) { pcSlice->setSliceType( P_SLICE ); } } #else if ( (pcSlice->getSliceType() == B_SLICE) && (pcSlice->getNumRefIdx(REF_PIC_LIST_1) == 0) ) { pcSlice->setSliceType ( P_SLICE ); } #endif pcSlice->setEncCABACTableIdx(m_pcSliceEncoder->getEncCABACTableIdx()); if (pcSlice->getSliceType() == B_SLICE) { pcSlice->setColFromL0Flag(1-uiColDir); Bool bLowDelay = true; Int iCurrPOC = pcSlice->getPOC(); Int iRefIdx = 0; for (iRefIdx = 0; iRefIdx < pcSlice->getNumRefIdx(REF_PIC_LIST_0) && bLowDelay; iRefIdx++) { if ( pcSlice->getRefPic(REF_PIC_LIST_0, iRefIdx)->getPOC() > iCurrPOC ) { bLowDelay = false; } } for (iRefIdx = 0; iRefIdx < pcSlice->getNumRefIdx(REF_PIC_LIST_1) && bLowDelay; iRefIdx++) { if ( pcSlice->getRefPic(REF_PIC_LIST_1, iRefIdx)->getPOC() > iCurrPOC ) { bLowDelay = false; } } pcSlice->setCheckLDC(bLowDelay); } else { pcSlice->setCheckLDC(true); } uiColDir = 1-uiColDir; //------------------------------------------------------------- pcSlice->setRefPOCList(); pcSlice->setList1IdxToList0Idx(); #if NH_3D_TMVP if(pcSlice->getLayerId()) pcSlice->generateAlterRefforTMVP(); #endif if (m_pcEncTop->getTMVPModeId() == 2) { if (iGOPid == 0) // first picture in SOP (i.e. forward B) { pcSlice->setEnableTMVPFlag(0); } else { // Note: pcSlice->getColFromL0Flag() is assumed to be always 0 and getcolRefIdx() is always 0. pcSlice->setEnableTMVPFlag(1); } } else if (m_pcEncTop->getTMVPModeId() == 1) { pcSlice->setEnableTMVPFlag(1); } else { pcSlice->setEnableTMVPFlag(0); } #if NH_MV if( pcSlice->getIdrPicFlag() ) { pcSlice->setEnableTMVPFlag(0); } #endif #if NH_3D_VSO // Should be moved to TEncTop !!! Bool bUseVSO = m_pcEncTop->getUseVSO(); TComRdCost* pcRdCost = m_pcEncTop->getRdCost(); pcRdCost->setUseVSO( bUseVSO ); // SAIT_VSO_EST_A0033 pcRdCost->setUseEstimatedVSD( m_pcEncTop->getUseEstimatedVSD() ); if ( bUseVSO ) { Int iVSOMode = m_pcEncTop->getVSOMode(); pcRdCost->setVSOMode( iVSOMode ); pcRdCost->setAllowNegDist( m_pcEncTop->getAllowNegDist() ); // SAIT_VSO_EST_A0033 #if H_3D_FCO Bool flagRec; flagRec = ((m_pcEncTop->getIvPicLists()->getPicYuv( pcSlice->getViewIndex(), false, pcSlice->getPOC(), true) == NULL) ? false: true); pcRdCost->setVideoRecPicYuv( m_pcEncTop->getIvPicLists()->getPicYuv( pcSlice->getViewIndex(), false, pcSlice->getPOC(), flagRec ) ); pcRdCost->setDepthPicYuv ( m_pcEncTop->getIvPicLists()->getPicYuv( pcSlice->getViewIndex(), true, pcSlice->getPOC(), false ) ); #else pcRdCost->setVideoRecPicYuv( m_pcEncTop->getIvPicLists()->getPicYuv( pcSlice->getViewIndex(), false , pcSlice->getPOC(), true ) ); pcRdCost->setDepthPicYuv ( m_pcEncTop->getIvPicLists()->getPicYuv( pcSlice->getViewIndex(), true , pcSlice->getPOC(), false ) ); #endif // LGE_WVSO_A0119 Bool bUseWVSO = m_pcEncTop->getUseWVSO(); pcRdCost->setUseWVSO( bUseWVSO ); } #endif /////////////////////////////////////////////////////////////////////////////////////////////////// Compress a slice // Slice compression if (m_pcCfg->getUseASR()) { m_pcSliceEncoder->setSearchRange(pcSlice); } Bool bGPBcheck=false; if ( pcSlice->getSliceType() == B_SLICE) { if ( pcSlice->getNumRefIdx(RefPicList( 0 ) ) == pcSlice->getNumRefIdx(RefPicList( 1 ) ) ) { bGPBcheck=true; Int i; for ( i=0; i < pcSlice->getNumRefIdx(RefPicList( 1 ) ); i++ ) { if ( pcSlice->getRefPOC(RefPicList(1), i) != pcSlice->getRefPOC(RefPicList(0), i) ) { bGPBcheck=false; break; } } } } if(bGPBcheck) { pcSlice->setMvdL1ZeroFlag(true); } else { pcSlice->setMvdL1ZeroFlag(false); } pcPic->getSlice(pcSlice->getSliceIdx())->setMvdL1ZeroFlag(pcSlice->getMvdL1ZeroFlag()); Double lambda = 0.0; Int actualHeadBits = 0; Int actualTotalBits = 0; Int estimatedBits = 0; Int tmpBitsBeforeWriting = 0; if ( m_pcCfg->getUseRateCtrl() ) // TODO: does this work with multiple slices and slice-segments? { Int frameLevel = m_pcRateCtrl->getRCSeq()->getGOPID2Level( iGOPid ); if ( pcPic->getSlice(0)->getSliceType() == I_SLICE ) { frameLevel = 0; } m_pcRateCtrl->initRCPic( frameLevel ); #if KWU_RC_MADPRED_E0227 if(m_pcCfg->getLayerId() != 0) { m_pcRateCtrl->getRCPic()->setIVPic( m_pcEncTop->getEncTop()->getTEncTop(0)->getRateCtrl()->getRCPic() ); } #endif estimatedBits = m_pcRateCtrl->getRCPic()->getTargetBits(); Int sliceQP = m_pcCfg->getInitialQP(); if ( ( pcSlice->getPOC() == 0 && m_pcCfg->getInitialQP() > 0 ) || ( frameLevel == 0 && m_pcCfg->getForceIntraQP() ) ) // QP is specified { Int NumberBFrames = ( m_pcCfg->getGOPSize() - 1 ); Double dLambda_scale = 1.0 - Clip3( 0.0, 0.5, 0.05*(Double)NumberBFrames ); Double dQPFactor = 0.57*dLambda_scale; Int SHIFT_QP = 12; Int bitdepth_luma_qp_scale = 0; Double qp_temp = (Double) sliceQP + bitdepth_luma_qp_scale - SHIFT_QP; lambda = dQPFactor*pow( 2.0, qp_temp/3.0 ); } else if ( frameLevel == 0 ) // intra case, but use the model { m_pcSliceEncoder->calCostSliceI(pcPic); // TODO: This only analyses the first slice segment - what about the others? if ( m_pcCfg->getIntraPeriod() != 1 ) // do not refine allocated bits for all intra case { Int bits = m_pcRateCtrl->getRCSeq()->getLeftAverageBits(); bits = m_pcRateCtrl->getRCPic()->getRefineBitsForIntra( bits ); if ( bits < 200 ) { bits = 200; } m_pcRateCtrl->getRCPic()->setTargetBits( bits ); } list listPreviousPicture = m_pcRateCtrl->getPicList(); m_pcRateCtrl->getRCPic()->getLCUInitTargetBits(); lambda = m_pcRateCtrl->getRCPic()->estimatePicLambda( listPreviousPicture, pcSlice->getSliceType()); sliceQP = m_pcRateCtrl->getRCPic()->estimatePicQP( lambda, listPreviousPicture ); } else // normal case { #if KWU_RC_MADPRED_E0227 if(m_pcRateCtrl->getLayerID() != 0) { list listPreviousPicture = m_pcRateCtrl->getPicList(); lambda = m_pcRateCtrl->getRCPic()->estimatePicLambdaIV( listPreviousPicture, pcSlice->getPOC() ); sliceQP = m_pcRateCtrl->getRCPic()->estimatePicQP( lambda, listPreviousPicture ); } else { #endif list listPreviousPicture = m_pcRateCtrl->getPicList(); lambda = m_pcRateCtrl->getRCPic()->estimatePicLambda( listPreviousPicture, pcSlice->getSliceType()); sliceQP = m_pcRateCtrl->getRCPic()->estimatePicQP( lambda, listPreviousPicture ); #if KWU_RC_MADPRED_E0227 } #endif } sliceQP = Clip3( -pcSlice->getSPS()->getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, sliceQP ); m_pcRateCtrl->getRCPic()->setPicEstQP( sliceQP ); m_pcSliceEncoder->resetQP( pcPic, sliceQP, lambda ); } UInt uiNumSliceSegments = 1; #if NH_3D_NBDV if(pcSlice->getViewIndex() && !pcSlice->getIsDepth()) //Notes from QC: this condition shall be changed once the configuration is completed, e.g. in pcSlice->getSPS()->getMultiviewMvPredMode() || ARP in prev. HTM. Remove this comment once it is done. { Int iColPoc = pcSlice->getRefPOC(RefPicList(1 - pcSlice->getColFromL0Flag()), pcSlice->getColRefIdx()); pcPic->setNumDdvCandPics(pcPic->getDisCandRefPictures(iColPoc)); } #endif #if NH_3D pcSlice->setDepthToDisparityLUTs(); #endif #if NH_3D_NBDV if(pcSlice->getViewIndex() && !pcSlice->getIsDepth() && !pcSlice->isIntra()) //Notes from QC: this condition shall be changed once the configuration is completed, e.g. in pcSlice->getSPS()->getMultiviewMvPredMode() || ARP in prev. HTM. Remove this comment once it is done. { pcPic->checkTemporalIVRef(); } if(pcSlice->getIsDepth()) { pcPic->checkTextureRef(); } #endif // Allocate some coders, now the number of tiles are known. const Int numSubstreamsColumns = (pcSlice->getPPS()->getNumTileColumnsMinus1() + 1); const Int numSubstreamRows = pcSlice->getPPS()->getEntropyCodingSyncEnabledFlag() ? pcPic->getFrameHeightInCtus() : (pcSlice->getPPS()->getNumTileRowsMinus1() + 1); const Int numSubstreams = numSubstreamRows * numSubstreamsColumns; std::vector substreamsOut(numSubstreams); // now compress (trial encode) the various slice segments (slices, and dependent slices) { const UInt numberOfCtusInFrame=pcPic->getPicSym()->getNumberOfCtusInFrame(); pcSlice->setSliceCurStartCtuTsAddr( 0 ); pcSlice->setSliceSegmentCurStartCtuTsAddr( 0 ); for(UInt nextCtuTsAddr = 0; nextCtuTsAddr < numberOfCtusInFrame; ) { m_pcSliceEncoder->precompressSlice( pcPic ); m_pcSliceEncoder->compressSlice ( pcPic, false, false ); const UInt curSliceSegmentEnd = pcSlice->getSliceSegmentCurEndCtuTsAddr(); if (curSliceSegmentEnd < numberOfCtusInFrame) { const Bool bNextSegmentIsDependentSlice=curSliceSegmentEndgetSliceCurEndCtuTsAddr(); const UInt sliceBits=pcSlice->getSliceBits(); pcPic->allocateNewSlice(); // prepare for next slice pcPic->setCurrSliceIdx ( uiNumSliceSegments ); m_pcSliceEncoder->setSliceIdx ( uiNumSliceSegments ); pcSlice = pcPic->getSlice ( uiNumSliceSegments ); assert(pcSlice->getPPS()!=0); pcSlice->copySliceInfo ( pcPic->getSlice(uiNumSliceSegments-1) ); pcSlice->setSliceIdx ( uiNumSliceSegments ); if (bNextSegmentIsDependentSlice) { pcSlice->setSliceBits(sliceBits); } else { pcSlice->setSliceCurStartCtuTsAddr ( curSliceSegmentEnd ); pcSlice->setSliceBits(0); } pcSlice->setDependentSliceSegmentFlag(bNextSegmentIsDependentSlice); pcSlice->setSliceSegmentCurStartCtuTsAddr ( curSliceSegmentEnd ); // TODO: optimise cabac_init during compress slice to improve multi-slice operation // pcSlice->setEncCABACTableIdx(m_pcSliceEncoder->getEncCABACTableIdx()); uiNumSliceSegments ++; } nextCtuTsAddr = curSliceSegmentEnd; } } duData.clear(); pcSlice = pcPic->getSlice(0); // SAO parameter estimation using non-deblocked pixels for CTU bottom and right boundary areas if( pcSlice->getSPS()->getUseSAO() && m_pcCfg->getSaoCtuBoundary() ) { m_pcSAO->getPreDBFStatistics(pcPic); } //-- Loop filter Bool bLFCrossTileBoundary = pcSlice->getPPS()->getLoopFilterAcrossTilesEnabledFlag(); m_pcLoopFilter->setCfg(bLFCrossTileBoundary); if ( m_pcCfg->getDeblockingFilterMetric() ) { applyDeblockingFilterMetric(pcPic, uiNumSliceSegments); } m_pcLoopFilter->loopFilterPic( pcPic ); /////////////////////////////////////////////////////////////////////////////////////////////////// File writing // Set entropy coder m_pcEntropyCoder->setEntropyCoder ( m_pcCavlcCoder ); if ( m_bSeqFirst ) { // write various parameter sets actualTotalBits += xWriteParameterSets(accessUnit, pcSlice); #if PPS_FIX_DEPTH if(!pcSlice->getIsDepth() || !pcSlice->getViewIndex() ) { #endif #if PPS_FIX_DEPTH } #endif // create prefix SEI messages at the beginning of the sequence assert(leadingSeiMessages.empty()); xCreateIRAPLeadingSEIMessages(leadingSeiMessages, pcSlice->getSPS(), pcSlice->getPPS()); m_bSeqFirst = false; } // reset presence of BP SEI indication m_bufferingPeriodSEIPresentInAU = false; // create prefix SEI associated with a picture xCreatePerPictureSEIMessages(iGOPid, leadingSeiMessages, nestedSeiMessages, pcSlice); /* use the main bitstream buffer for storing the marshalled picture */ m_pcEntropyCoder->setBitstream(NULL); pcSlice = pcPic->getSlice(0); if (pcSlice->getSPS()->getUseSAO()) { Bool sliceEnabled[MAX_NUM_COMPONENT]; TComBitCounter tempBitCounter; tempBitCounter.resetBits(); m_pcEncTop->getRDGoOnSbacCoder()->setBitstream(&tempBitCounter); m_pcSAO->initRDOCabacCoder(m_pcEncTop->getRDGoOnSbacCoder(), pcSlice); m_pcSAO->SAOProcess(pcPic, sliceEnabled, pcPic->getSlice(0)->getLambdas(), m_pcCfg->getTestSAODisableAtPictureLevel(), m_pcCfg->getSaoEncodingRate(), m_pcCfg->getSaoEncodingRateChroma(), m_pcCfg->getSaoCtuBoundary()); m_pcSAO->PCMLFDisableProcess(pcPic); m_pcEncTop->getRDGoOnSbacCoder()->setBitstream(NULL); //assign SAO slice header for(Int s=0; s< uiNumSliceSegments; s++) { pcPic->getSlice(s)->setSaoEnabledFlag(CHANNEL_TYPE_LUMA, sliceEnabled[COMPONENT_Y]); assert(sliceEnabled[COMPONENT_Cb] == sliceEnabled[COMPONENT_Cr]); pcPic->getSlice(s)->setSaoEnabledFlag(CHANNEL_TYPE_CHROMA, sliceEnabled[COMPONENT_Cb]); } } // pcSlice is currently slice 0. std::size_t binCountsInNalUnits = 0; // For implementation of cabac_zero_word stuffing (section 7.4.3.10) std::size_t numBytesInVclNalUnits = 0; // For implementation of cabac_zero_word stuffing (section 7.4.3.10) for( UInt sliceSegmentStartCtuTsAddr = 0, sliceIdxCount=0; sliceSegmentStartCtuTsAddr < pcPic->getPicSym()->getNumberOfCtusInFrame(); sliceIdxCount++, sliceSegmentStartCtuTsAddr=pcSlice->getSliceSegmentCurEndCtuTsAddr() ) { pcSlice = pcPic->getSlice(sliceIdxCount); if(sliceIdxCount > 0 && pcSlice->getSliceType()!= I_SLICE) { pcSlice->checkColRefIdx(sliceIdxCount, pcPic); } pcPic->setCurrSliceIdx(sliceIdxCount); m_pcSliceEncoder->setSliceIdx(sliceIdxCount); pcSlice->setRPS(pcPic->getSlice(0)->getRPS()); pcSlice->setRPSidx(pcPic->getSlice(0)->getRPSidx()); for ( UInt ui = 0 ; ui < numSubstreams; ui++ ) { substreamsOut[ui].clear(); } m_pcEntropyCoder->setEntropyCoder ( m_pcCavlcCoder ); m_pcEntropyCoder->resetEntropy ( pcSlice ); /* start slice NALunit */ #if NH_MV OutputNALUnit nalu( pcSlice->getNalUnitType(), pcSlice->getTLayer(), getLayerId() ); #else OutputNALUnit nalu( pcSlice->getNalUnitType(), pcSlice->getTLayer() ); #endif m_pcEntropyCoder->setBitstream(&nalu.m_Bitstream); pcSlice->setNoRaslOutputFlag(false); if (pcSlice->isIRAP()) { if (pcSlice->getNalUnitType() >= NAL_UNIT_CODED_SLICE_BLA_W_LP && pcSlice->getNalUnitType() <= NAL_UNIT_CODED_SLICE_IDR_N_LP) { pcSlice->setNoRaslOutputFlag(true); } //the inference for NoOutputPriorPicsFlag // KJS: This cannot happen at the encoder if (!m_bFirst && pcSlice->isIRAP() && pcSlice->getNoRaslOutputFlag()) { if (pcSlice->getNalUnitType() == NAL_UNIT_CODED_SLICE_CRA) { pcSlice->setNoOutputPriorPicsFlag(true); } } } pcSlice->setEncCABACTableIdx(m_pcSliceEncoder->getEncCABACTableIdx()); tmpBitsBeforeWriting = m_pcEntropyCoder->getNumberOfWrittenBits(); m_pcEntropyCoder->encodeSliceHeader(pcSlice); actualHeadBits += ( m_pcEntropyCoder->getNumberOfWrittenBits() - tmpBitsBeforeWriting ); pcSlice->setFinalized(true); pcSlice->clearSubstreamSizes( ); { UInt numBinsCoded = 0; m_pcSliceEncoder->encodeSlice(pcPic, &(substreamsOut[0]), numBinsCoded); binCountsInNalUnits+=numBinsCoded; } { // Construct the final bitstream by concatenating substreams. // The final bitstream is either nalu.m_Bitstream or pcBitstreamRedirect; // Complete the slice header info. m_pcEntropyCoder->setEntropyCoder ( m_pcCavlcCoder ); m_pcEntropyCoder->setBitstream(&nalu.m_Bitstream); m_pcEntropyCoder->encodeTilesWPPEntryPoint( pcSlice ); // Append substreams... TComOutputBitstream *pcOut = pcBitstreamRedirect; const Int numZeroSubstreamsAtStartOfSlice = pcPic->getSubstreamForCtuAddr(pcSlice->getSliceSegmentCurStartCtuTsAddr(), false, pcSlice); const Int numSubstreamsToCode = pcSlice->getNumberOfSubstreamSizes()+1; for ( UInt ui = 0 ; ui < numSubstreamsToCode; ui++ ) { pcOut->addSubstream(&(substreamsOut[ui+numZeroSubstreamsAtStartOfSlice])); } } // If current NALU is the first NALU of slice (containing slice header) and more NALUs exist (due to multiple dependent slices) then buffer it. // If current NALU is the last NALU of slice and a NALU was buffered, then (a) Write current NALU (b) Update an write buffered NALU at approproate location in NALU list. Bool bNALUAlignedWrittenToList = false; // used to ensure current NALU is not written more than once to the NALU list. xAttachSliceDataToNalUnit(nalu, pcBitstreamRedirect); accessUnit.push_back(new NALUnitEBSP(nalu)); actualTotalBits += UInt(accessUnit.back()->m_nalUnitData.str().size()) * 8; numBytesInVclNalUnits += (std::size_t)(accessUnit.back()->m_nalUnitData.str().size()); bNALUAlignedWrittenToList = true; if (!bNALUAlignedWrittenToList) { nalu.m_Bitstream.writeAlignZero(); accessUnit.push_back(new NALUnitEBSP(nalu)); } if( ( m_pcCfg->getPictureTimingSEIEnabled() || m_pcCfg->getDecodingUnitInfoSEIEnabled() ) && ( pcSlice->getSPS()->getVuiParametersPresentFlag() ) && ( ( pcSlice->getSPS()->getVuiParameters()->getHrdParameters()->getNalHrdParametersPresentFlag() ) || ( pcSlice->getSPS()->getVuiParameters()->getHrdParameters()->getVclHrdParametersPresentFlag() ) ) && ( pcSlice->getSPS()->getVuiParameters()->getHrdParameters()->getSubPicCpbParamsPresentFlag() ) ) { UInt numNalus = 0; UInt numRBSPBytes = 0; for (AccessUnit::const_iterator it = accessUnit.begin(); it != accessUnit.end(); it++) { numRBSPBytes += UInt((*it)->m_nalUnitData.str().size()); numNalus ++; } duData.push_back(DUData()); duData.back().accumBitsDU = ( numRBSPBytes << 3 ); duData.back().accumNalsDU = numNalus; } } // end iteration over slices // cabac_zero_words processing cabac_zero_word_padding(pcSlice, pcPic, binCountsInNalUnits, numBytesInVclNalUnits, accessUnit.back()->m_nalUnitData, m_pcCfg->getCabacZeroWordPaddingEnabled()); #if NH_3D pcPic->compressMotion(2); #else pcPic->compressMotion(); #endif #if NH_MV m_pocLastCoded = pcPic->getPOC(); #endif //-- For time output for each slice Double dEncTime = (Double)(clock()-iBeforeTime) / CLOCKS_PER_SEC; std::string digestStr; if (m_pcCfg->getDecodedPictureHashSEIEnabled()) { SEIDecodedPictureHash *decodedPictureHashSei = new SEIDecodedPictureHash(); m_seiEncoder.initDecodedPictureHashSEI(decodedPictureHashSei, pcPic, digestStr, pcSlice->getSPS()->getBitDepths()); trailingSeiMessages.push_back(decodedPictureHashSei); } xWriteTrailingSEIMessages(trailingSeiMessages, accessUnit, pcSlice->getTLayer(), pcSlice->getSPS()); m_pcCfg->setEncodedFlag(iGOPid, true); xCalculateAddPSNRs( isField, isTff, iGOPid, pcPic, accessUnit, rcListPic, dEncTime, snr_conversion, printFrameMSE ); if (!digestStr.empty()) { if(m_pcCfg->getDecodedPictureHashSEIEnabled() == 1) { printf(" [MD5:%s]", digestStr.c_str()); } else if(m_pcCfg->getDecodedPictureHashSEIEnabled() == 2) { printf(" [CRC:%s]", digestStr.c_str()); } else if(m_pcCfg->getDecodedPictureHashSEIEnabled() == 3) { printf(" [Checksum:%s]", digestStr.c_str()); } } if ( m_pcCfg->getUseRateCtrl() ) { Double avgQP = m_pcRateCtrl->getRCPic()->calAverageQP(); Double avgLambda = m_pcRateCtrl->getRCPic()->calAverageLambda(); if ( avgLambda < 0.0 ) { avgLambda = lambda; } m_pcRateCtrl->getRCPic()->updateAfterPicture( actualHeadBits, actualTotalBits, avgQP, avgLambda, pcSlice->getSliceType()); m_pcRateCtrl->getRCPic()->addToPictureLsit( m_pcRateCtrl->getPicList() ); m_pcRateCtrl->getRCSeq()->updateAfterPic( actualTotalBits ); if ( pcSlice->getSliceType() != I_SLICE ) { m_pcRateCtrl->getRCGOP()->updateAfterPicture( actualTotalBits ); } else // for intra picture, the estimated bits are used to update the current status in the GOP { m_pcRateCtrl->getRCGOP()->updateAfterPicture( estimatedBits ); } } xCreatePictureTimingSEI(m_pcCfg->getEfficientFieldIRAPEnabled()?effFieldIRAPMap.GetIRAPGOPid():0, leadingSeiMessages, nestedSeiMessages, duInfoSeiMessages, pcSlice, isField, duData); if (m_pcCfg->getScalableNestingSEIEnabled()) { xCreateScalableNestingSEI (leadingSeiMessages, nestedSeiMessages); } xWriteLeadingSEIMessages(leadingSeiMessages, duInfoSeiMessages, accessUnit, pcSlice->getTLayer(), pcSlice->getSPS(), duData); xWriteDuSEIMessages(duInfoSeiMessages, accessUnit, pcSlice->getTLayer(), pcSlice->getSPS(), duData); pcPic->getPicYuvRec()->copyToPic(pcPicYuvRecOut); pcPic->setReconMark ( true ); #if NH_MV TComSlice::markIvRefPicsAsShortTerm( m_refPicSetInterLayer0, m_refPicSetInterLayer1 ); std::vector temp; TComSlice::markCurrPic( pcPic ); #endif m_bFirst = false; m_iNumPicCoded++; m_totalCoded ++; /* logging: insert a newline at end of picture period */ printf("\n"); fflush(stdout); if (m_pcCfg->getEfficientFieldIRAPEnabled()) { iGOPid=effFieldIRAPMap.restoreGOPid(iGOPid); } } // iGOPid-loop delete pcBitstreamRedirect; #if !NH_MV assert ( (m_iNumPicCoded == iNumPicRcvd) ); #endif } Void TEncGOP::printOutSummary(UInt uiNumAllPicCoded, Bool isField, const Bool printMSEBasedSNR, const Bool printSequenceMSE, const BitDepths &bitDepths) { assert (uiNumAllPicCoded == m_gcAnalyzeAll.getNumPic()); //--CFG_KDY const Int rateMultiplier=(isField?2:1); m_gcAnalyzeAll.setFrmRate( m_pcCfg->getFrameRate()*rateMultiplier ); m_gcAnalyzeI.setFrmRate( m_pcCfg->getFrameRate()*rateMultiplier ); m_gcAnalyzeP.setFrmRate( m_pcCfg->getFrameRate()*rateMultiplier ); m_gcAnalyzeB.setFrmRate( m_pcCfg->getFrameRate()*rateMultiplier ); const ChromaFormat chFmt = m_pcCfg->getChromaFormatIdc(); //-- all #if NH_MV printf( "\n\nSUMMARY -------------------------------------------- LayerId %2d\n", getLayerId() ); #else printf( "\n\nSUMMARY --------------------------------------------------------\n" ); #endif m_gcAnalyzeAll.printOut('a', chFmt, printMSEBasedSNR, printSequenceMSE, bitDepths); printf( "\n\nI Slices--------------------------------------------------------\n" ); m_gcAnalyzeI.printOut('i', chFmt, printMSEBasedSNR, printSequenceMSE, bitDepths); printf( "\n\nP Slices--------------------------------------------------------\n" ); m_gcAnalyzeP.printOut('p', chFmt, printMSEBasedSNR, printSequenceMSE, bitDepths); printf( "\n\nB Slices--------------------------------------------------------\n" ); m_gcAnalyzeB.printOut('b', chFmt, printMSEBasedSNR, printSequenceMSE, bitDepths); if (!m_pcCfg->getSummaryOutFilename().empty()) { m_gcAnalyzeAll.printSummary(chFmt, printSequenceMSE, bitDepths, m_pcCfg->getSummaryOutFilename()); } if (!m_pcCfg->getSummaryPicFilenameBase().empty()) { m_gcAnalyzeI.printSummary(chFmt, printSequenceMSE, bitDepths, m_pcCfg->getSummaryPicFilenameBase()+"I.txt"); m_gcAnalyzeP.printSummary(chFmt, printSequenceMSE, bitDepths, m_pcCfg->getSummaryPicFilenameBase()+"P.txt"); m_gcAnalyzeB.printSummary(chFmt, printSequenceMSE, bitDepths, m_pcCfg->getSummaryPicFilenameBase()+"B.txt"); } if(isField) { //-- interlaced summary m_gcAnalyzeAll_in.setFrmRate( m_pcCfg->getFrameRate()); m_gcAnalyzeAll_in.setBits(m_gcAnalyzeAll.getBits()); // prior to the above statement, the interlace analyser does not contain the correct total number of bits. printf( "\n\nSUMMARY INTERLACED ---------------------------------------------\n" ); m_gcAnalyzeAll_in.printOut('a', chFmt, printMSEBasedSNR, printSequenceMSE, bitDepths); if (!m_pcCfg->getSummaryOutFilename().empty()) { m_gcAnalyzeAll_in.printSummary(chFmt, printSequenceMSE, bitDepths, m_pcCfg->getSummaryOutFilename()); } } printf("\nRVM: %.3lf\n" , xCalculateRVM()); } #if NH_3D_VSO Void TEncGOP::preLoopFilterPicAll( TComPic* pcPic, Dist64& ruiDist ) #else Void TEncGOP::preLoopFilterPicAll( TComPic* pcPic, UInt64& ruiDist ) #endif { Bool bCalcDist = false; m_pcLoopFilter->setCfg(m_pcCfg->getLFCrossTileBoundaryFlag()); m_pcLoopFilter->loopFilterPic( pcPic ); if (!bCalcDist) { ruiDist = xFindDistortionFrame(pcPic->getPicYuvOrg(), pcPic->getPicYuvRec(), pcPic->getPicSym()->getSPS().getBitDepths()); } } // ==================================================================================================================== // Protected member functions // ==================================================================================================================== Void TEncGOP::xInitGOP( Int iPOCLast, Int iNumPicRcvd, Bool isField ) { assert( iNumPicRcvd > 0 ); // Exception for the first frames if ( ( isField && (iPOCLast == 0 || iPOCLast == 1) ) || (!isField && (iPOCLast == 0)) ) { m_iGopSize = 1; } else { m_iGopSize = m_pcCfg->getGOPSize(); } assert (m_iGopSize > 0); return; } Void TEncGOP::xGetBuffer( TComList& rcListPic, TComList& rcListPicYuvRecOut, Int iNumPicRcvd, Int iTimeOffset, TComPic*& rpcPic, TComPicYuv*& rpcPicYuvRecOut, Int pocCurr, Bool isField) { Int i; // Rec. output TComList::iterator iterPicYuvRec = rcListPicYuvRecOut.end(); if (isField && pocCurr > 1 && m_iGopSize!=1) { iTimeOffset--; } for ( i = 0; i < (iNumPicRcvd - iTimeOffset + 1); i++ ) { iterPicYuvRec--; } rpcPicYuvRecOut = *(iterPicYuvRec); // Current pic. TComList::iterator iterPic = rcListPic.begin(); while (iterPic != rcListPic.end()) { rpcPic = *(iterPic); rpcPic->setCurrSliceIdx(0); if (rpcPic->getPOC() == pocCurr) { break; } iterPic++; } #if !NH_MV assert (rpcPic != NULL); #endif assert (rpcPic->getPOC() == pocCurr); return; } #if NH_3D_VSO Dist64 TEncGOP::xFindDistortionFrame (TComPicYuv* pcPic0, TComPicYuv* pcPic1, const BitDepths &bitDepths) #else UInt64 TEncGOP::xFindDistortionFrame (TComPicYuv* pcPic0, TComPicYuv* pcPic1, const BitDepths &bitDepths) #endif { #if NH_3D_VSO Dist64 uiTotalDiff = 0; #else UInt64 uiTotalDiff = 0; #endif for(Int chan=0; changetNumberValidComponents(); chan++) { const ComponentID ch=ComponentID(chan); Pel* pSrc0 = pcPic0 ->getAddr(ch); Pel* pSrc1 = pcPic1 ->getAddr(ch); UInt uiShift = 2 * DISTORTION_PRECISION_ADJUSTMENT(bitDepths.recon[toChannelType(ch)]-8); const Int iStride = pcPic0->getStride(ch); const Int iWidth = pcPic0->getWidth(ch); const Int iHeight = pcPic0->getHeight(ch); for(Int y = 0; y < iHeight; y++ ) { for(Int x = 0; x < iWidth; x++ ) { Intermediate_Int iTemp = pSrc0[x] - pSrc1[x]; uiTotalDiff += UInt64((iTemp*iTemp) >> uiShift); } pSrc0 += iStride; pSrc1 += iStride; } } return uiTotalDiff; } Void TEncGOP::xCalculateAddPSNRs( const Bool isField, const Bool isFieldTopFieldFirst, const Int iGOPid, TComPic* pcPic, const AccessUnit&accessUnit, TComList &rcListPic, const Double dEncTime, const InputColourSpaceConversion snr_conversion, const Bool printFrameMSE ) { xCalculateAddPSNR( pcPic, pcPic->getPicYuvRec(), accessUnit, dEncTime, snr_conversion, printFrameMSE ); //In case of field coding, compute the interlaced PSNR for both fields if(isField) { Bool bothFieldsAreEncoded = false; Int correspondingFieldPOC = pcPic->getPOC(); Int currentPicGOPPoc = m_pcCfg->getGOPEntry(iGOPid).m_POC; if(pcPic->getPOC() == 0) { // particular case for POC 0 and 1. // If they are not encoded first and separately from other pictures, we need to change this // POC 0 is always encoded first then POC 1 is encoded bothFieldsAreEncoded = false; } else if(pcPic->getPOC() == 1) { // if we are at POC 1, POC 0 has been encoded for sure correspondingFieldPOC = 0; bothFieldsAreEncoded = true; } else { if(pcPic->getPOC()%2 == 1) { correspondingFieldPOC -= 1; // all odd POC are associated with the preceding even POC (e.g poc 1 is associated to poc 0) currentPicGOPPoc -= 1; } else { correspondingFieldPOC += 1; // all even POC are associated with the following odd POC (e.g poc 0 is associated to poc 1) currentPicGOPPoc += 1; } for(Int i = 0; i < m_iGopSize; i ++) { if(m_pcCfg->getGOPEntry(i).m_POC == currentPicGOPPoc) { bothFieldsAreEncoded = m_pcCfg->getGOPEntry(i).m_isEncoded; break; } } } if(bothFieldsAreEncoded) { //get complementary top field TComList::iterator iterPic = rcListPic.begin(); while ((*iterPic)->getPOC() != correspondingFieldPOC) { iterPic ++; } TComPic* correspondingFieldPic = *(iterPic); if( (pcPic->isTopField() && isFieldTopFieldFirst) || (!pcPic->isTopField() && !isFieldTopFieldFirst)) { xCalculateInterlacedAddPSNR(pcPic, correspondingFieldPic, pcPic->getPicYuvRec(), correspondingFieldPic->getPicYuvRec(), snr_conversion, printFrameMSE ); } else { xCalculateInterlacedAddPSNR(correspondingFieldPic, pcPic, correspondingFieldPic->getPicYuvRec(), pcPic->getPicYuvRec(), snr_conversion, printFrameMSE ); } } } } Void TEncGOP::xCalculateAddPSNR( TComPic* pcPic, TComPicYuv* pcPicD, const AccessUnit& accessUnit, Double dEncTime, const InputColourSpaceConversion conversion, const Bool printFrameMSE ) { Double dPSNR[MAX_NUM_COMPONENT]; for(Int i=0; igetWidth(COMPONENT_Y), pcPicD->getHeight(COMPONENT_Y), pcPicD->getChromaFormat(), pcPicD->getWidth(COMPONENT_Y), pcPicD->getHeight(COMPONENT_Y), 0, false); TVideoIOYuv::ColourSpaceConvert(*pcPicD, cscd, conversion, false); } TComPicYuv &picd=(conversion==IPCOLOURSPACE_UNCHANGED)?*pcPicD : cscd; //===== calculate PSNR ===== Double MSEyuvframe[MAX_NUM_COMPONENT] = {0, 0, 0}; for(Int chan=0; changetNumberValidComponents(); chan++) { const ComponentID ch=ComponentID(chan); const TComPicYuv *pOrgPicYuv =(conversion!=IPCOLOURSPACE_UNCHANGED) ? pcPic ->getPicYuvTrueOrg() : pcPic ->getPicYuvOrg(); const Pel* pOrg = pOrgPicYuv->getAddr(ch); const Int iOrgStride = pOrgPicYuv->getStride(ch); Pel* pRec = picd.getAddr(ch); const Int iRecStride = picd.getStride(ch); const Int iWidth = pcPicD->getWidth (ch) - (m_pcEncTop->getPad(0) >> pcPic->getComponentScaleX(ch)); const Int iHeight = pcPicD->getHeight(ch) - ((m_pcEncTop->getPad(1) >> (pcPic->isField()?1:0)) >> pcPic->getComponentScaleY(ch)); Int iSize = iWidth*iHeight; UInt64 uiSSDtemp=0; for(Int y = 0; y < iHeight; y++ ) { for(Int x = 0; x < iWidth; x++ ) { Intermediate_Int iDiff = (Intermediate_Int)( pOrg[x] - pRec[x] ); uiSSDtemp += iDiff * iDiff; } pOrg += iOrgStride; pRec += iRecStride; } #if NH_3D_VSO #if H_3D_VSO_SYNTH_DIST_OUT if ( m_pcRdCost->getUseRenModel() ) { unsigned int maxval = 255 * (1<<(g_uiBitDepth + g_uiBitIncrement -8)); Double fRefValueY = (double) maxval * maxval * iSize; Double fRefValueC = fRefValueY / 4.0; TRenModel* pcRenModel = m_pcEncTop->getEncTop()->getRenModel(); Int64 iDistVSOY, iDistVSOU, iDistVSOV; pcRenModel->getTotalSSE( iDistVSOY, iDistVSOU, iDistVSOV ); dYPSNR = ( iDistVSOY ? 10.0 * log10( fRefValueY / (Double) iDistVSOY ) : 99.99 ); dUPSNR = ( iDistVSOU ? 10.0 * log10( fRefValueC / (Double) iDistVSOU ) : 99.99 ); dVPSNR = ( iDistVSOV ? 10.0 * log10( fRefValueC / (Double) iDistVSOV ) : 99.99 ); } else { #endif #endif const Int maxval = 255 << (pcPic->getPicSym()->getSPS().getBitDepth(toChannelType(ch)) - 8); const Double fRefValue = (Double) maxval * maxval * iSize; dPSNR[ch] = ( uiSSDtemp ? 10.0 * log10( fRefValue / (Double)uiSSDtemp ) : 999.99 ); MSEyuvframe[ch] = (Double)uiSSDtemp/(iSize); } #if NH_3D_VSO #if H_3D_VSO_SYNTH_DIST_OUT } #endif #endif /* calculate the size of the access unit, excluding: * - any AnnexB contributions (start_code_prefix, zero_byte, etc.,) * - SEI NAL units */ UInt numRBSPBytes = 0; for (AccessUnit::const_iterator it = accessUnit.begin(); it != accessUnit.end(); it++) { UInt numRBSPBytes_nal = UInt((*it)->m_nalUnitData.str().size()); if (m_pcCfg->getSummaryVerboseness() > 0) { printf("*** %6s numBytesInNALunit: %u\n", nalUnitTypeToString((*it)->m_nalUnitType), numRBSPBytes_nal); } if ((*it)->m_nalUnitType != NAL_UNIT_PREFIX_SEI && (*it)->m_nalUnitType != NAL_UNIT_SUFFIX_SEI) { numRBSPBytes += numRBSPBytes_nal; } } UInt uibits = numRBSPBytes * 8; m_vRVM_RP.push_back( uibits ); //===== add PSNR ===== m_gcAnalyzeAll.addResult (dPSNR, (Double)uibits, MSEyuvframe); TComSlice* pcSlice = pcPic->getSlice(0); if (pcSlice->isIntra()) { m_gcAnalyzeI.addResult (dPSNR, (Double)uibits, MSEyuvframe); } if (pcSlice->isInterP()) { m_gcAnalyzeP.addResult (dPSNR, (Double)uibits, MSEyuvframe); } if (pcSlice->isInterB()) { m_gcAnalyzeB.addResult (dPSNR, (Double)uibits, MSEyuvframe); } Char c = (pcSlice->isIntra() ? 'I' : pcSlice->isInterP() ? 'P' : 'B'); if (!pcSlice->isReferenced()) { c += 32; } #if ADAPTIVE_QP_SELECTION #if NH_MV printf("Layer %3d POC %4d TId: %1d ( %c-SLICE, nQP %d QP %d ) %10d bits", pcSlice->getLayerId(), pcSlice->getPOC(), pcSlice->getTLayer(), c, pcSlice->getSliceQpBase(), pcSlice->getSliceQp(), uibits ); #else printf("POC %4d TId: %1d ( %c-SLICE, nQP %d QP %d ) %10d bits", pcSlice->getPOC(), pcSlice->getTLayer(), c, pcSlice->getSliceQpBase(), pcSlice->getSliceQp(), uibits ); #endif #else #if NH_MV printf("Layer %3d POC %4d TId: %1d ( %c-SLICE, QP %d ) %10d bits", pcSlice->getLayerId(), pcSlice->getPOC()-pcSlice->getLastIDR(), pcSlice->getTLayer(), c, pcSlice->getSliceQp(), uibits ); #else printf("POC %4d TId: %1d ( %c-SLICE, QP %d ) %10d bits", pcSlice->getPOC()-pcSlice->getLastIDR(), pcSlice->getTLayer(), c, pcSlice->getSliceQp(), uibits ); #endif #endif #if NH_MV printf(" [Y %8.4lf dB U %8.4lf dB V %8.4lf dB]", dPSNR[COMPONENT_Y], dPSNR[COMPONENT_Cb], dPSNR[COMPONENT_Cr] ); #else printf(" [Y %6.4lf dB U %6.4lf dB V %6.4lf dB]", dPSNR[COMPONENT_Y], dPSNR[COMPONENT_Cb], dPSNR[COMPONENT_Cr] ); #endif if (printFrameMSE) { printf(" [Y MSE %6.4lf U MSE %6.4lf V MSE %6.4lf]", MSEyuvframe[COMPONENT_Y], MSEyuvframe[COMPONENT_Cb], MSEyuvframe[COMPONENT_Cr] ); } printf(" [ET %5.0f ]", dEncTime ); for (Int iRefList = 0; iRefList < 2; iRefList++) { printf(" [L%d ", iRefList); for (Int iRefIndex = 0; iRefIndex < pcSlice->getNumRefIdx(RefPicList(iRefList)); iRefIndex++) { #if NH_MV if( pcSlice->getLayerId() != pcSlice->getRefLayerId( RefPicList(iRefList), iRefIndex ) ) { printf( "V%d ", pcSlice->getRefLayerId( RefPicList(iRefList), iRefIndex ) ); } else { #endif printf ("%d ", pcSlice->getRefPOC(RefPicList(iRefList), iRefIndex)-pcSlice->getLastIDR()); #if NH_MV } #endif } printf("]"); } cscd.destroy(); } Void TEncGOP::xCalculateInterlacedAddPSNR( TComPic* pcPicOrgFirstField, TComPic* pcPicOrgSecondField, TComPicYuv* pcPicRecFirstField, TComPicYuv* pcPicRecSecondField, const InputColourSpaceConversion conversion, const Bool printFrameMSE ) { #if NH_MV assert( 0 ); // Field coding and MV need to be aligned. #else const TComSPS &sps=pcPicOrgFirstField->getPicSym()->getSPS(); Double dPSNR[MAX_NUM_COMPONENT]; TComPic *apcPicOrgFields[2]={pcPicOrgFirstField, pcPicOrgSecondField}; TComPicYuv *apcPicRecFields[2]={pcPicRecFirstField, pcPicRecSecondField}; for(Int i=0; igetChromaFormat()==apcPicRecFields[1]->getChromaFormat()); const UInt numValidComponents=apcPicRecFields[0]->getNumberValidComponents(); for(Int chan=0; changetWidth(ch)==apcPicRecFields[1]->getWidth(ch)); assert(apcPicRecFields[0]->getHeight(ch)==apcPicRecFields[1]->getHeight(ch)); UInt64 uiSSDtemp=0; const Int iWidth = apcPicRecFields[0]->getWidth (ch) - (m_pcEncTop->getPad(0) >> apcPicRecFields[0]->getComponentScaleX(ch)); const Int iHeight = apcPicRecFields[0]->getHeight(ch) - ((m_pcEncTop->getPad(1) >> 1) >> apcPicRecFields[0]->getComponentScaleY(ch)); Int iSize = iWidth*iHeight; for(UInt fieldNum=0; fieldNum<2; fieldNum++) { TComPic *pcPic=apcPicOrgFields[fieldNum]; TComPicYuv *pcPicD=apcPicRecFields[fieldNum]; const Pel* pOrg = (conversion!=IPCOLOURSPACE_UNCHANGED) ? pcPic ->getPicYuvTrueOrg()->getAddr(ch) : pcPic ->getPicYuvOrg()->getAddr(ch); Pel* pRec = pcPicD->getAddr(ch); const Int iStride = pcPicD->getStride(ch); for(Int y = 0; y < iHeight; y++ ) { for(Int x = 0; x < iWidth; x++ ) { Intermediate_Int iDiff = (Intermediate_Int)( pOrg[x] - pRec[x] ); uiSSDtemp += iDiff * iDiff; } pOrg += iStride; pRec += iStride; } } const Int maxval = 255 << (sps.getBitDepth(toChannelType(ch)) - 8); const Double fRefValue = (Double) maxval * maxval * iSize*2; dPSNR[ch] = ( uiSSDtemp ? 10.0 * log10( fRefValue / (Double)uiSSDtemp ) : 999.99 ); MSEyuvframe[ch] = (Double)uiSSDtemp/(iSize*2); } UInt uibits = 0; // the number of bits for the pair is not calculated here - instead the overall total is used elsewhere. //===== add PSNR ===== m_gcAnalyzeAll_in.addResult (dPSNR, (Double)uibits, MSEyuvframe); printf("\n Interlaced frame %d: [Y %6.4lf dB U %6.4lf dB V %6.4lf dB]", pcPicOrgSecondField->getPOC()/2 , dPSNR[COMPONENT_Y], dPSNR[COMPONENT_Cb], dPSNR[COMPONENT_Cr] ); if (printFrameMSE) { printf(" [Y MSE %6.4lf U MSE %6.4lf V MSE %6.4lf]", MSEyuvframe[COMPONENT_Y], MSEyuvframe[COMPONENT_Cb], MSEyuvframe[COMPONENT_Cr] ); } for(UInt fieldNum=0; fieldNum<2; fieldNum++) { cscd[fieldNum].destroy(); } #endif } /** Function for deciding the nal_unit_type. * \param pocCurr POC of the current picture * \param lastIDR POC of the last IDR picture * \param isField true to indicate field coding * \returns the NAL unit type of the picture * This function checks the configuration and returns the appropriate nal_unit_type for the picture. */ NalUnitType TEncGOP::getNalUnitType(Int pocCurr, Int lastIDR, Bool isField) { if (pocCurr == 0) { return NAL_UNIT_CODED_SLICE_IDR_W_RADL; } if(m_pcCfg->getEfficientFieldIRAPEnabled() && isField && pocCurr == 1) { // to avoid the picture becoming an IRAP return NAL_UNIT_CODED_SLICE_TRAIL_R; } if(m_pcCfg->getDecodingRefreshType() != 3 && (pocCurr - isField) % m_pcCfg->getIntraPeriod() == 0) { if (m_pcCfg->getDecodingRefreshType() == 1) { return NAL_UNIT_CODED_SLICE_CRA; } else if (m_pcCfg->getDecodingRefreshType() == 2) { return NAL_UNIT_CODED_SLICE_IDR_W_RADL; } } if(m_pocCRA>0) { if(pocCurr0) { if (pocCurr < lastIDR) { return NAL_UNIT_CODED_SLICE_RADL_R; } } return NAL_UNIT_CODED_SLICE_TRAIL_R; } Double TEncGOP::xCalculateRVM() { Double dRVM = 0; if( m_pcCfg->getGOPSize() == 1 && m_pcCfg->getIntraPeriod() != 1 && m_pcCfg->getFramesToBeEncoded() > RVM_VCEGAM10_M * 2 ) { // calculate RVM only for lowdelay configurations std::vector vRL , vB; size_t N = m_vRVM_RP.size(); vRL.resize( N ); vB.resize( N ); Int i; Double dRavg = 0 , dBavg = 0; vB[RVM_VCEGAM10_M] = 0; for( i = RVM_VCEGAM10_M + 1 ; i < N - RVM_VCEGAM10_M + 1 ; i++ ) { vRL[i] = 0; for( Int j = i - RVM_VCEGAM10_M ; j <= i + RVM_VCEGAM10_M - 1 ; j++ ) { vRL[i] += m_vRVM_RP[j]; } vRL[i] /= ( 2 * RVM_VCEGAM10_M ); vB[i] = vB[i-1] + m_vRVM_RP[i] - vRL[i]; dRavg += m_vRVM_RP[i]; dBavg += vB[i]; } dRavg /= ( N - 2 * RVM_VCEGAM10_M ); dBavg /= ( N - 2 * RVM_VCEGAM10_M ); Double dSigamB = 0; for( i = RVM_VCEGAM10_M + 1 ; i < N - RVM_VCEGAM10_M + 1 ; i++ ) { Double tmp = vB[i] - dBavg; dSigamB += tmp * tmp; } dSigamB = sqrt( dSigamB / ( N - 2 * RVM_VCEGAM10_M ) ); Double f = sqrt( 12.0 * ( RVM_VCEGAM10_M - 1 ) / ( RVM_VCEGAM10_M + 1 ) ); dRVM = dSigamB / dRavg * f; } return( dRVM ); } /** Attaches the input bitstream to the stream in the output NAL unit Updates rNalu to contain concatenated bitstream. rpcBitstreamRedirect is cleared at the end of this function call. * \param codedSliceData contains the coded slice data (bitstream) to be concatenated to rNalu * \param rNalu target NAL unit */ Void TEncGOP::xAttachSliceDataToNalUnit (OutputNALUnit& rNalu, TComOutputBitstream* codedSliceData) { // Byte-align rNalu.m_Bitstream.writeByteAlignment(); // Slice header byte-alignment // Perform bitstream concatenation if (codedSliceData->getNumberOfWrittenBits() > 0) { rNalu.m_Bitstream.addSubstream(codedSliceData); } m_pcEntropyCoder->setBitstream(&rNalu.m_Bitstream); codedSliceData->clear(); } // Function will arrange the long-term pictures in the decreasing order of poc_lsb_lt, // and among the pictures with the same lsb, it arranges them in increasing delta_poc_msb_cycle_lt value Void TEncGOP::arrangeLongtermPicturesInRPS(TComSlice *pcSlice, TComList& rcListPic) { if(pcSlice->getRPS()->getNumberOfLongtermPictures() == 0) { return; } // we can only modify the local RPS! assert (pcSlice->getRPSidx()==-1); TComReferencePictureSet *rps = pcSlice->getLocalRPS(); // Arrange long-term reference pictures in the correct order of LSB and MSB, // and assign values for pocLSBLT and MSB present flag Int longtermPicsPoc[MAX_NUM_REF_PICS], longtermPicsLSB[MAX_NUM_REF_PICS], indices[MAX_NUM_REF_PICS]; Int longtermPicsMSB[MAX_NUM_REF_PICS]; Bool mSBPresentFlag[MAX_NUM_REF_PICS]; ::memset(longtermPicsPoc, 0, sizeof(longtermPicsPoc)); // Store POC values of LTRP ::memset(longtermPicsLSB, 0, sizeof(longtermPicsLSB)); // Store POC LSB values of LTRP ::memset(longtermPicsMSB, 0, sizeof(longtermPicsMSB)); // Store POC LSB values of LTRP ::memset(indices , 0, sizeof(indices)); // Indices to aid in tracking sorted LTRPs ::memset(mSBPresentFlag , 0, sizeof(mSBPresentFlag)); // Indicate if MSB needs to be present // Get the long-term reference pictures Int offset = rps->getNumberOfNegativePictures() + rps->getNumberOfPositivePictures(); Int i, ctr = 0; Int maxPicOrderCntLSB = 1 << pcSlice->getSPS()->getBitsForPOC(); for(i = rps->getNumberOfPictures() - 1; i >= offset; i--, ctr++) { longtermPicsPoc[ctr] = rps->getPOC(i); // LTRP POC longtermPicsLSB[ctr] = getLSB(longtermPicsPoc[ctr], maxPicOrderCntLSB); // LTRP POC LSB indices[ctr] = i; longtermPicsMSB[ctr] = longtermPicsPoc[ctr] - longtermPicsLSB[ctr]; } Int numLongPics = rps->getNumberOfLongtermPictures(); assert(ctr == numLongPics); // Arrange pictures in decreasing order of MSB; for(i = 0; i < numLongPics; i++) { for(Int j = 0; j < numLongPics - 1; j++) { if(longtermPicsMSB[j] < longtermPicsMSB[j+1]) { std::swap(longtermPicsPoc[j], longtermPicsPoc[j+1]); std::swap(longtermPicsLSB[j], longtermPicsLSB[j+1]); std::swap(longtermPicsMSB[j], longtermPicsMSB[j+1]); std::swap(indices[j] , indices[j+1] ); } } } for(i = 0; i < numLongPics; i++) { // Check if MSB present flag should be enabled. // Check if the buffer contains any pictures that have the same LSB. TComList::iterator iterPic = rcListPic.begin(); TComPic* pcPic; while ( iterPic != rcListPic.end() ) { pcPic = *iterPic; if( (getLSB(pcPic->getPOC(), maxPicOrderCntLSB) == longtermPicsLSB[i]) && // Same LSB (pcPic->getSlice(0)->isReferenced()) && // Reference picture (pcPic->getPOC() != longtermPicsPoc[i]) ) // Not the LTRP itself { mSBPresentFlag[i] = true; break; } iterPic++; } } // tempArray for usedByCurr flag Bool tempArray[MAX_NUM_REF_PICS]; ::memset(tempArray, 0, sizeof(tempArray)); for(i = 0; i < numLongPics; i++) { tempArray[i] = rps->getUsed(indices[i]); } // Now write the final values; ctr = 0; Int currMSB = 0, currLSB = 0; // currPicPoc = currMSB + currLSB currLSB = getLSB(pcSlice->getPOC(), maxPicOrderCntLSB); currMSB = pcSlice->getPOC() - currLSB; for(i = rps->getNumberOfPictures() - 1; i >= offset; i--, ctr++) { rps->setPOC (i, longtermPicsPoc[ctr]); rps->setDeltaPOC (i, - pcSlice->getPOC() + longtermPicsPoc[ctr]); rps->setUsed (i, tempArray[ctr]); rps->setPocLSBLT (i, longtermPicsLSB[ctr]); rps->setDeltaPocMSBCycleLT (i, (currMSB - (longtermPicsPoc[ctr] - longtermPicsLSB[ctr])) / maxPicOrderCntLSB); rps->setDeltaPocMSBPresentFlag(i, mSBPresentFlag[ctr]); assert(rps->getDeltaPocMSBCycleLT(i) >= 0); // Non-negative value } for(i = rps->getNumberOfPictures() - 1, ctr = 1; i >= offset; i--, ctr++) { for(Int j = rps->getNumberOfPictures() - 1 - ctr; j >= offset; j--) { // Here at the encoder we know that we have set the full POC value for the LTRPs, hence we // don't have to check the MSB present flag values for this constraint. assert( rps->getPOC(i) != rps->getPOC(j) ); // If assert fails, LTRP entry repeated in RPS!!! } } } Void TEncGOP::applyDeblockingFilterMetric( TComPic* pcPic, UInt uiNumSlices ) { TComPicYuv* pcPicYuvRec = pcPic->getPicYuvRec(); Pel* Rec = pcPicYuvRec->getAddr(COMPONENT_Y); Pel* tempRec = Rec; Int stride = pcPicYuvRec->getStride(COMPONENT_Y); UInt log2maxTB = pcPic->getSlice(0)->getSPS()->getQuadtreeTULog2MaxSize(); UInt maxTBsize = (1<getWidth(COMPONENT_Y); const UInt picHeight = pcPicYuvRec->getHeight(COMPONENT_Y); const UInt noCol = (picWidth>>log2maxTB); const UInt noRows = (picHeight>>log2maxTB); assert(noCol > 1); assert(noRows > 1); UInt64 *colSAD = (UInt64*)malloc(noCol*sizeof(UInt64)); UInt64 *rowSAD = (UInt64*)malloc(noRows*sizeof(UInt64)); UInt colIdx = 0; UInt rowIdx = 0; Pel p0, p1, p2, q0, q1, q2; Int qp = pcPic->getSlice(0)->getSliceQp(); const Int bitDepthLuma=pcPic->getSlice(0)->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA); Int bitdepthScale = 1 << (bitDepthLuma-8); Int beta = TComLoopFilter::getBeta( qp ) * bitdepthScale; const Int thr2 = (beta>>2); const Int thr1 = 2*bitdepthScale; UInt a = 0; memset(colSAD, 0, noCol*sizeof(UInt64)); memset(rowSAD, 0, noRows*sizeof(UInt64)); if (maxTBsize > minBlockArtSize) { // Analyze vertical artifact edges for(Int c = maxTBsize; c < picWidth; c += maxTBsize) { for(Int r = 0; r < picHeight; r++) { p2 = Rec[c-3]; p1 = Rec[c-2]; p0 = Rec[c-1]; q0 = Rec[c]; q1 = Rec[c+1]; q2 = Rec[c+2]; a = ((abs(p2-(p1<<1)+p0)+abs(q0-(q1<<1)+q2))<<1); if ( thr1 < a && a < thr2) { colSAD[colIdx] += abs(p0 - q0); } Rec += stride; } colIdx++; Rec = tempRec; } // Analyze horizontal artifact edges for(Int r = maxTBsize; r < picHeight; r += maxTBsize) { for(Int c = 0; c < picWidth; c++) { p2 = Rec[c + (r-3)*stride]; p1 = Rec[c + (r-2)*stride]; p0 = Rec[c + (r-1)*stride]; q0 = Rec[c + r*stride]; q1 = Rec[c + (r+1)*stride]; q2 = Rec[c + (r+2)*stride]; a = ((abs(p2-(p1<<1)+p0)+abs(q0-(q1<<1)+q2))<<1); if (thr1 < a && a < thr2) { rowSAD[rowIdx] += abs(p0 - q0); } } rowIdx++; } } UInt64 colSADsum = 0; UInt64 rowSADsum = 0; for(Int c = 0; c < noCol-1; c++) { colSADsum += colSAD[c]; } for(Int r = 0; r < noRows-1; r++) { rowSADsum += rowSAD[r]; } colSADsum <<= 10; rowSADsum <<= 10; colSADsum /= (noCol-1); colSADsum /= picHeight; rowSADsum /= (noRows-1); rowSADsum /= picWidth; UInt64 avgSAD = ((colSADsum + rowSADsum)>>1); avgSAD >>= (bitDepthLuma-8); if ( avgSAD > 2048 ) { avgSAD >>= 9; Int offset = Clip3(2,6,(Int)avgSAD); for (Int i=0; igetSlice(i)->setDeblockingFilterOverrideFlag(true); pcPic->getSlice(i)->setDeblockingFilterDisable(false); pcPic->getSlice(i)->setDeblockingFilterBetaOffsetDiv2( offset ); pcPic->getSlice(i)->setDeblockingFilterTcOffsetDiv2( offset ); } } else { for (Int i=0; igetSlice(i)->setDeblockingFilterOverrideFlag(false); pcPic->getSlice(i)->setDeblockingFilterDisable( pcPic->getSlice(i)->getPPS()->getPicDisableDeblockingFilterFlag() ); pcPic->getSlice(i)->setDeblockingFilterBetaOffsetDiv2( pcPic->getSlice(i)->getPPS()->getDeblockingFilterBetaOffsetDiv2() ); pcPic->getSlice(i)->setDeblockingFilterTcOffsetDiv2( pcPic->getSlice(i)->getPPS()->getDeblockingFilterTcOffsetDiv2() ); } } free(colSAD); free(rowSAD); } #if NH_MV Void TEncGOP::xSetRefPicListModificationsMv( std::vector tempPicLists[2], TComSlice* pcSlice, UInt iGOPid ) { if( pcSlice->getSliceType() == I_SLICE || !(pcSlice->getPPS()->getListsModificationPresentFlag()) || pcSlice->getNumActiveRefLayerPics() == 0 ) { return; } GOPEntry ge = m_pcCfg->getGOPEntry( (pcSlice->getRapPicFlag() && ( pcSlice->getLayerId( ) > 0) ) ? MAX_GOP : iGOPid ); assert( ge.m_numActiveRefLayerPics == pcSlice->getNumActiveRefLayerPics() ); Int numPicsInTempList = pcSlice->getNumRpsCurrTempList(); // GT: check if SliceType should be checked here. for (Int li = 0; li < 2; li ++) // Loop over lists L0 and L1 { Int numPicsInFinalRefList = pcSlice->getNumRefIdx( ( li == 0 ) ? REF_PIC_LIST_0 : REF_PIC_LIST_1 ); Int finalIdxToTempIdxMap[16]; for( Int k = 0; k < 16; k++ ) { finalIdxToTempIdxMap[ k ] = -1; } Bool isModified = false; if ( numPicsInTempList > 1 ) { for( Int k = 0; k < pcSlice->getNumActiveRefLayerPics(); k++ ) { // get position in temp. list Int refPicLayerId = pcSlice->getRefPicLayerId(k); Int idxInTempList = 0; for (; idxInTempList < numPicsInTempList; idxInTempList++) { if ( (tempPicLists[li][idxInTempList])->getLayerId() == refPicLayerId ) { break; } } Int idxInFinalList = ge.m_interViewRefPosL[ li ][ k ]; // Add negative from behind idxInFinalList = ( idxInFinalList < 0 )? ( numPicsInTempList + idxInFinalList ) : idxInFinalList; Bool curIsModified = ( idxInFinalList != idxInTempList ) && ( ( idxInTempList < numPicsInFinalRefList ) || ( idxInFinalList < numPicsInFinalRefList ) ) ; if ( curIsModified ) { isModified = true; assert( finalIdxToTempIdxMap[ idxInFinalList ] == -1 ); // Assert when two inter layer reference pictures are sorted to the same position } finalIdxToTempIdxMap[ idxInFinalList ] = idxInTempList; } } TComRefPicListModification* refPicListModification = pcSlice->getRefPicListModification(); refPicListModification->setRefPicListModificationFlagL( li, isModified ); if( isModified ) { Int refIdx = 0; for( Int i = 0; i < numPicsInFinalRefList; i++ ) { if( finalIdxToTempIdxMap[i] >= 0 ) { refPicListModification->setRefPicSetIdxL( li, i, finalIdxToTempIdxMap[i] ); } else { ///* Fill gaps with temporal references */// // Forward inter layer reference pictures while( ( refIdx < numPicsInTempList ) && ( tempPicLists[li][refIdx]->getLayerId() != getLayerId()) ) { refIdx++; } refPicListModification->setRefPicSetIdxL( li, i, refIdx ); refIdx++; } } } } } #endif //! \}