diff --git a/doc/software-manual.tex b/doc/software-manual.tex index e2dbefb6a4da155cef2574c85571dcd90070821b..ddc4700e57d53ae99824fadf4d8637d65cb64212 100644 --- a/doc/software-manual.tex +++ b/doc/software-manual.tex @@ -3767,7 +3767,18 @@ SEI messages. \begin{OptionTableNoShorthand}{Scalable nesting SEI message encoder parameters}{tab:sei-scalable-nesting} \Option{SEIScalableNesting} & \Default{0} & -Enables or disables the use of the scalable nesting SEI messages. +Enables creation of scalable nesting SEI messages for buffering period and picture timing SEI messages. +\\ +\Option{SubpicDecodedPictureHash} & +\Default{0} & +Enables creation of decoded picture hash SEI messages for each subpicture and writes these in scalable nesting SEI messages. +\par +\begin{tabular}{cp{0.35\textwidth}} + 0 & Disabled \\ + 1 & MD5 \\ + 2 & CRCs \\ + 3 & checksum \\ +\end{tabular} \\ \end{OptionTableNoShorthand} diff --git a/source/App/BitstreamExtractorApp/BitstreamExtractorApp.cpp b/source/App/BitstreamExtractorApp/BitstreamExtractorApp.cpp index 8e4bf3ca10f146d62498d7a4c61f7f589b38edc9..8b3b68720dbf9f5d2695e00dd5347df697019991 100644 --- a/source/App/BitstreamExtractorApp/BitstreamExtractorApp.cpp +++ b/source/App/BitstreamExtractorApp/BitstreamExtractorApp.cpp @@ -350,6 +350,10 @@ void BitstreamExtractorApp::xWriteVPS(VPS *vps, std::ostream& out, int layerId, m_hlSyntaxWriter.setBitstream( &naluOut.m_Bitstream ); m_hlSyntaxWriter.codeVPS( vps ); +#if JVET_R0294_SUBPIC_HASH + NALUnitEBSP naluWithHeader(naluOut); + writeAnnexBNalUnit(out, naluWithHeader, true); +#else // create a dummy AU AccessUnit tmpAu; // convert to EBSP (this adds emulation prevention!) and add into NAL unit @@ -361,6 +365,7 @@ void BitstreamExtractorApp::xWriteVPS(VPS *vps, std::ostream& out, int layerId, // AU works without chaning the start code length. // This cannot be done for VLC NAL units! writeAnnexB (out, tmpAu); +#endif } void BitstreamExtractorApp::xWriteSPS(SPS *sps, std::ostream& out, int layerId, int temporalId) @@ -373,6 +378,10 @@ void BitstreamExtractorApp::xWriteSPS(SPS *sps, std::ostream& out, int layerId, m_hlSyntaxWriter.setBitstream( &naluOut.m_Bitstream ); m_hlSyntaxWriter.codeSPS( sps ); +#if JVET_R0294_SUBPIC_HASH + NALUnitEBSP naluWithHeader(naluOut); + writeAnnexBNalUnit(out, naluWithHeader, true); +#else // create a dummy AU AccessUnit tmpAu; // convert to EBSP (this adds emulation prevention!) and add into NAL unit @@ -384,6 +393,7 @@ void BitstreamExtractorApp::xWriteSPS(SPS *sps, std::ostream& out, int layerId, // AU works without chaning the start code length. // This cannot be done for VLC NAL units! writeAnnexB (out, tmpAu); +#endif } void BitstreamExtractorApp::xWritePPS(PPS *pps, std::ostream& out, int layerId, int temporalId) @@ -395,6 +405,10 @@ void BitstreamExtractorApp::xWritePPS(PPS *pps, std::ostream& out, int layerId, m_hlSyntaxWriter.setBitstream( &naluOut.m_Bitstream ); m_hlSyntaxWriter.codePPS( pps ); +#if JVET_R0294_SUBPIC_HASH + NALUnitEBSP naluWithHeader(naluOut); + writeAnnexBNalUnit(out, naluWithHeader, true); +#else // create a dummy AU AccessUnit tmpAu; // convert to EBSP (this adds emulation prevention!) and add into NAL unit @@ -406,6 +420,7 @@ void BitstreamExtractorApp::xWritePPS(PPS *pps, std::ostream& out, int layerId, // AU works without chaning the start code length. // This cannot be done for VLC NAL units! writeAnnexB (out, tmpAu); +#endif } // returns true, if the NAL unit is to be discarded @@ -425,6 +440,48 @@ bool BitstreamExtractorApp::xCheckNumSubLayers(InputNALUnit &nalu, VPS *vps) return retval; } +#if JVET_R0294_SUBPIC_HASH +bool BitstreamExtractorApp::xCheckSEIsSubPicture(SEIMessages& SEIs, InputNALUnit& nalu, std::ostream& out) +{ + SEIMessages scalableNestingSEIs = getSeisByType(SEIs, SEI::SCALABLE_NESTING); + if (scalableNestingSEIs.size()) + { + CHECK ( scalableNestingSEIs.size() > 1, "There shall be only one Scalable Nesting SEI in one NAL unit" ); + CHECK ( scalableNestingSEIs.size() != SEIs.size(), "Scalable Nesting SEI shall not be in the same NAL unit as other SEIs" ); + // check, if the scalable nesting SEI applies to the target subpicture + SEIScalableNesting *sei = (SEIScalableNesting*) scalableNestingSEIs.front(); + + if (sei->m_snSubpicFlag == 0) + { + // does not apply to a subpicture -> remove + return false; + } + if (std::find(sei->m_snSubpicId.begin(), sei->m_snSubpicId.end(), m_subPicId) != sei->m_snSubpicId.end()) + { + // applies to target subpicture -> extract + OutputNALUnit outNalu( nalu.m_nalUnitType, nalu.m_nuhLayerId, nalu.m_temporalId ); + m_seiWriter.writeSEImessages(outNalu.m_Bitstream, sei->m_nestedSEIs, m_hrd, false, nalu.m_temporalId); + NALUnitEBSP naluWithHeader(outNalu); + writeAnnexBNalUnit(out, naluWithHeader, true); + return false; + } + else + { + // does not apply to target subpicture -> remove + return false; + } + } + // remove not nested decoded picture hash SEIs + SEIMessages hashSEI = getSeisByType(SEIs, SEI::DECODED_PICTURE_HASH); + if (hashSEI.size() > 0) + { + return false; + } + // keep all other SEIs + return true; +} +#endif + uint32_t BitstreamExtractorApp::decode() { std::ifstream bitstreamFileIn(m_bitstreamFileNameIn.c_str(), std::ifstream::in | std::ifstream::binary); @@ -648,17 +705,28 @@ uint32_t BitstreamExtractorApp::decode() { xReadPicHeader(nalu); } +#if JVET_R0294_SUBPIC_HASH + if ( (nalu.m_nalUnitType == NAL_UNIT_PREFIX_SEI) || (nalu.m_nalUnitType == NAL_UNIT_SUFFIX_SEI)) + { +#else if (m_targetOlsIdx>=0) { if (nalu.m_nalUnitType == NAL_UNIT_PREFIX_SEI) { - // decoding a SEI - SEIMessages SEIs; - HRD hrd; - m_seiReader.parseSEImessage(&(nalu.getBitstream()), SEIs, nalu.m_nalUnitType, nalu.m_nuhLayerId, nalu.m_temporalId, vps, m_parameterSetManager.getActiveSPS(), hrd, &std::cout); +#endif + // decode SEI + SEIMessages SEIs; +#if !JVET_R0294_SUBPIC_HASH + HRD hrd; + m_seiReader.parseSEImessage(&(nalu.getBitstream()), SEIs, nalu.m_nalUnitType, nalu.m_nuhLayerId, nalu.m_temporalId, vps, m_parameterSetManager.getActiveSPS(), hrd, &std::cout); +#else + m_seiReader.parseSEImessage(&(nalu.getBitstream()), SEIs, nalu.m_nalUnitType, nalu.m_nuhLayerId, nalu.m_temporalId, vps, m_parameterSetManager.getActiveSPS(), m_hrd, &std::cout); + if (m_targetOlsIdx>=0) + { +#endif for (auto sei : SEIs) { - // remove unqualiified scalable nesting SEI + // remove unqualified scalable nesting SEI if (sei->payloadType() == SEI::SCALABLE_NESTING) { SEIScalableNesting *seiNesting = (SEIScalableNesting *)sei; @@ -683,12 +751,22 @@ uint32_t BitstreamExtractorApp::decode() writeInpuNalUnitToStream &= !targetOlsIdxGreaterThanZero; } } +#if !JVET_R0294_SUBPIC_HASH + } +#endif + if (m_vpsId == -1) + { + delete vps; + } } - if (m_vpsId == -1) +#if JVET_R0294_SUBPIC_HASH + if (m_subPicId>=0) { - delete vps; + writeInpuNalUnitToStream &= xCheckSEIsSubPicture(SEIs, nalu, bitstreamFileOut); } } +#endif + #if JVET_R0107_BITSTREAM_EXTACTION Slice slice; if (nalu.isSlice()) diff --git a/source/App/BitstreamExtractorApp/BitstreamExtractorApp.h b/source/App/BitstreamExtractorApp/BitstreamExtractorApp.h index 748189bb3df717701d1e03e3b05258cfd5cd37c1..9e087052ba2fc696c28e0d5efc57b3004c10933d 100644 --- a/source/App/BitstreamExtractorApp/BitstreamExtractorApp.h +++ b/source/App/BitstreamExtractorApp/BitstreamExtractorApp.h @@ -50,7 +50,9 @@ #include "VLCWriter.h" #include "SEIread.h" - +#if JVET_R0294_SUBPIC_HASH +#include "SEIwrite.h" +#endif class BitstreamExtractorApp : public BitstreamExtractorAppCfg { @@ -73,6 +75,9 @@ protected: bool xCheckSliceSubpicture(InputNALUnit &nalu, int subPicId); #endif void xReadPicHeader(InputNALUnit &nalu); +#if JVET_R0294_SUBPIC_HASH + bool xCheckSEIsSubPicture(SEIMessages& SEIs, InputNALUnit& nalu, std::ostream& out); +#endif void xSetSPSUpdated(int spsId) { return m_updatedSPSList.push_back(spsId); } bool xIsSPSUpdate(int spsId) { return (std::find(m_updatedSPSList.begin(),m_updatedSPSList.end(), spsId) != m_updatedSPSList.end()); } @@ -88,6 +93,11 @@ protected: HLSyntaxReader m_hlSynaxReader; HLSWriter m_hlSyntaxWriter; SEIReader m_seiReader; +#if JVET_R0294_SUBPIC_HASH + SEIWriter m_seiWriter; + HRD m_hrd; +#endif + int m_vpsId; bool m_removeTimingSEI; diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp index efcfbc0a05e0a72209b3b3a8b04be2db554cc498..af077e021d1511d06ab5a2fa7f97763f513935f0 100644 --- a/source/App/EncoderApp/EncApp.cpp +++ b/source/App/EncoderApp/EncApp.cpp @@ -785,6 +785,9 @@ void EncApp::xInitLibCfg() m_cEncLib.setSaoGreedyMergeEnc ( m_saoGreedyMergeEnc); m_cEncLib.setIntraSmoothingDisabledFlag (!m_enableIntraReferenceSmoothing ); m_cEncLib.setDecodedPictureHashSEIType ( m_decodedPictureHashSEIType ); +#if JVET_R0294_SUBPIC_HASH + m_cEncLib.setSubpicDecodedPictureHashType ( m_subpicDecodedPictureHashType ); +#endif m_cEncLib.setDependentRAPIndicationSEIEnabled ( m_drapPeriod > 0 ); m_cEncLib.setBufferingPeriodSEIEnabled ( m_bufferingPeriodSEIEnabled ); m_cEncLib.setPictureTimingSEIEnabled ( m_pictureTimingSEIEnabled ); @@ -1328,7 +1331,11 @@ void EncApp::xWriteOutput( int iNumEncoded, std::list<PelUnitBuf*>& recBufList ) void EncApp::outputAU( const AccessUnit& au ) { +#if JVET_R0294_SUBPIC_HASH + const vector<uint32_t>& stats = writeAnnexBAccessUnit(m_bitstream, au); +#else const vector<uint32_t>& stats = writeAnnexB(m_bitstream, au); +#endif rateStatsAccum(au, stats); m_bitstream.flush(); } diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp index 0b71aa07e0fbe2699bce4df329df7ff5281ea339..a07a813b085b86d0876e5cf21b79eb0904e7a753 100644 --- a/source/App/EncoderApp/EncAppCfg.cpp +++ b/source/App/EncoderApp/EncAppCfg.cpp @@ -559,6 +559,9 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] ) int tmpFastInterSearchMode; int tmpMotionEstimationSearchMethod; int tmpDecodedPictureHashSEIMappedType; +#if JVET_R0294_SUBPIC_HASH + int tmpSubpicDecodedPictureHashMappedType; +#endif string inputColourSpaceConvert; string inputPathPrefix; ExtendedProfileName extendedProfile; @@ -1147,6 +1150,13 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] ) "\t2: CRC\n" "\t1: use MD5\n" "\t0: disable") +#if JVET_R0294_SUBPIC_HASH + ("SubpicDecodedPictureHash", tmpSubpicDecodedPictureHashMappedType, 0, "Control generation of decode picture hash SEI messages for each subpicture\n" + "\t3: checksum\n" + "\t2: CRC\n" + "\t1: use MD5\n" + "\t0: disable") +#endif ("TMVPMode", m_TMVPModeId, 1, "TMVP mode 0: TMVP disable for all slices. 1: TMVP enable for all slices (default) 2: TMVP enable for certain slices only") ("SliceLevelRpl", m_sliceLevelRpl, true, "Code reference picture lists in slice headers rather than picture header.") ("SliceLevelDblk", m_sliceLevelDblk, true, "Code deblocking filter parameters in slice headers rather than picture header.") @@ -1959,7 +1969,17 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] ) { m_decodedPictureHashSEIType=HashType(tmpDecodedPictureHashSEIMappedType-1); } - +#if JVET_R0294_SUBPIC_HASH + // Need to map values to match those of the SEI message: + if (tmpSubpicDecodedPictureHashMappedType==0) + { + m_subpicDecodedPictureHashType=HASHTYPE_NONE; + } + else + { + m_subpicDecodedPictureHashType=HashType(tmpSubpicDecodedPictureHashMappedType-1); + } +#endif // allocate slice-based dQP values m_aidQP = new int[ m_framesToBeEncoded + m_iGOPSize + 1 ]; ::memset( m_aidQP, 0, sizeof(int)*( m_framesToBeEncoded + m_iGOPSize + 1 ) ); diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h index 810978a126af34b91f92964c8efa519db8acbd87..745f9214c233f8273df9cffa719da0859ae1eee7 100644 --- a/source/App/EncoderApp/EncAppCfg.h +++ b/source/App/EncoderApp/EncAppCfg.h @@ -483,6 +483,9 @@ protected: bool m_bUseBLambdaForNonKeyLowDelayPictures; HashType m_decodedPictureHashSEIType; ///< Checksum mode for decoded picture hash SEI message +#if JVET_R0294_SUBPIC_HASH + HashType m_subpicDecodedPictureHashType; +#endif bool m_bufferingPeriodSEIEnabled; bool m_pictureTimingSEIEnabled; bool m_bpDeltasGOPStructure; diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h index 4eb7af104b34782ddfbcec84210ce6a61f1749a0..659c8f93a59136a0d3770c465995ac45c2b8b020 100644 --- a/source/Lib/CommonLib/TypeDef.h +++ b/source/Lib/CommonLib/TypeDef.h @@ -113,6 +113,8 @@ #define JVET_S0185_PROPOSAL2_SEI_CLEANUP 1 // JVET-S0185_PROPOSAL2: Move signalling of syntax element bp_alt_cpb_params_present_flag +#define JVET_R0294_SUBPIC_HASH 1 // JVET-R0294: Allow decoded picture hash SEI messages to be nested in subpicture context + #define JVET_S0181_PROPOSAL1 1 // JVET-0181_Proposal1: Conditionally signal bp_sublayer_initial_cpb_removal_delay_present_flag #define JVET_Q0406_CABAC_ZERO 1 // JVET-Q0406: signal cabac_zero_words per sub-picture diff --git a/source/Lib/DecoderLib/SEIread.cpp b/source/Lib/DecoderLib/SEIread.cpp index bf641c4851ee242ba63d5e5c11295e0c5390b147..608f84febdb4834dc840971618ff1519100143b4 100644 --- a/source/Lib/DecoderLib/SEIread.cpp +++ b/source/Lib/DecoderLib/SEIread.cpp @@ -326,6 +326,12 @@ void SEIReader::xReadSEImessage(SEIMessages& seis, const NalUnitType nalUnitType sei = new SEIDecodedPictureHash; xParseSEIDecodedPictureHash((SEIDecodedPictureHash&) *sei, payloadSize, pDecodedMessageOutputStream); break; +#if JVET_R0294_SUBPIC_HASH + case SEI::SCALABLE_NESTING: + sei = new SEIScalableNesting; + xParseSEIScalableNesting((SEIScalableNesting&)*sei, nalUnitType, nuh_layer_id, payloadSize, vps, sps, pDecodedMessageOutputStream); + break; +#endif default: for (uint32_t i = 0; i < payloadSize; i++) { diff --git a/source/Lib/EncoderLib/AnnexBwrite.h b/source/Lib/EncoderLib/AnnexBwrite.h index 03477b243e36b1cd55c86d791261b336e81d53d6..d891bf47a55d4b6230dc0e78b653d94712dbebd0 100644 --- a/source/Lib/EncoderLib/AnnexBwrite.h +++ b/source/Lib/EncoderLib/AnnexBwrite.h @@ -42,6 +42,30 @@ //! \ingroup EncoderLib //! \{ +#if JVET_R0294_SUBPIC_HASH +uint32_t writeAnnexBNalUnit(std::ostream& out, const NALUnitEBSP& nalu, bool useLongStartcode) +{ + uint32_t size = 0; /* size of annexB unit in bytes */ + + static const uint8_t startCodePrefix[] = {0,0,0,1}; + + if (useLongStartcode) + { + out.write(reinterpret_cast<const char*>(startCodePrefix), 4); + size += 4; + } + else + { + out.write(reinterpret_cast<const char*>(startCodePrefix+1), 3); + size += 3; + } + out << nalu.m_nalUnitData.str(); + size += uint32_t(nalu.m_nalUnitData.str().size()); + + return size; +} +#endif + /** * write all NALunits in au to bytestream out in a manner satisfying * AnnexB of AVC. NALunits are written in the order they are found in au. @@ -49,13 +73,18 @@ * - the initial startcode in the access unit, * - any SPS/PPS nal units */ +#if JVET_R0294_SUBPIC_HASH +std::vector<uint32_t> writeAnnexBAccessUnit(std::ostream& out, const AccessUnit& au) +#else static std::vector<uint32_t> writeAnnexB(std::ostream& out, const AccessUnit& au) +#endif { std::vector<uint32_t> annexBsizes; for (AccessUnit::const_iterator it = au.begin(); it != au.end(); it++) { const NALUnitEBSP& nalu = **it; +#if !JVET_R0294_SUBPIC_HASH uint32_t size = 0; /* size of annexB unit in bytes */ static const uint8_t start_code_prefix[] = {0,0,0,1}; @@ -81,7 +110,12 @@ static std::vector<uint32_t> writeAnnexB(std::ostream& out, const AccessUnit& au } out << nalu.m_nalUnitData.str(); size += uint32_t(nalu.m_nalUnitData.str().size()); +#else + const bool useLongStartCode = (it == au.begin() || nalu.m_nalUnitType == NAL_UNIT_DCI || nalu.m_nalUnitType == NAL_UNIT_VPS || nalu.m_nalUnitType == NAL_UNIT_SPS + || nalu.m_nalUnitType == NAL_UNIT_PPS || nalu.m_nalUnitType == NAL_UNIT_PREFIX_APS || nalu.m_nalUnitType == NAL_UNIT_SUFFIX_APS); + const uint32_t size = writeAnnexBNalUnit(out, nalu, useLongStartCode); +#endif annexBsizes.push_back(size); } @@ -92,6 +126,7 @@ static std::vector<uint32_t> writeAnnexB(std::ostream& out, const AccessUnit& au return annexBsizes; } + //! \} #endif diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h index f432632772a57fdd75344d3ddd3d423d83fa1746..ad9e4b0d2549b2a646c9206604720db14cc60c65 100644 --- a/source/Lib/EncoderLib/EncCfg.h +++ b/source/Lib/EncoderLib/EncCfg.h @@ -512,6 +512,9 @@ protected: bool m_entryPointPresentFlag; ///< flag for the presence of entry points HashType m_decodedPictureHashSEIType; +#if JVET_R0294_SUBPIC_HASH + HashType m_subpicDecodedPictureHashType; +#endif bool m_bufferingPeriodSEIEnabled; bool m_pictureTimingSEIEnabled; bool m_frameFieldInfoSEIEnabled; @@ -1470,6 +1473,11 @@ public: void setEntryPointPresentFlag(bool b) { m_entryPointPresentFlag = b; } void setDecodedPictureHashSEIType(HashType m) { m_decodedPictureHashSEIType = m; } HashType getDecodedPictureHashSEIType() const { return m_decodedPictureHashSEIType; } +#if JVET_R0294_SUBPIC_HASH + void setSubpicDecodedPictureHashType(HashType m) { m_subpicDecodedPictureHashType = m; } + HashType getSubpicDecodedPictureHashType() const { return m_subpicDecodedPictureHashType; } +#endif + void setBufferingPeriodSEIEnabled(bool b) { m_bufferingPeriodSEIEnabled = b; } bool getBufferingPeriodSEIEnabled() const { return m_bufferingPeriodSEIEnabled; } void setPictureTimingSEIEnabled(bool b) { m_pictureTimingSEIEnabled = b; } diff --git a/source/Lib/EncoderLib/EncGOP.cpp b/source/Lib/EncoderLib/EncGOP.cpp index c5975257d76870d48ac324d629740abccacad220..0ca0ef69d1e8dfabbec175792c5de2667d89a69c 100644 --- a/source/Lib/EncoderLib/EncGOP.cpp +++ b/source/Lib/EncoderLib/EncGOP.cpp @@ -752,7 +752,11 @@ void EncGOP::xCreatePerPictureSEIMessages (int picInGOP, SEIMessages& seiMessage } +#if JVET_R0294_SUBPIC_HASH +void EncGOP::xCreateScalableNestingSEI(SEIMessages& seiMessages, SEIMessages& nestedSeiMessages, const std::vector<int> &targetOLSs, const std::vector<int> &targetLayers, const std::vector<uint16_t>& subpicIDs) +#else void EncGOP::xCreateScalableNestingSEI(SEIMessages& seiMessages, SEIMessages& nestedSeiMessages, const std::vector<uint16_t>& subpicIDs) +#endif { SEIMessages tmpMessages; while (!nestedSeiMessages.empty()) @@ -761,7 +765,11 @@ void EncGOP::xCreateScalableNestingSEI(SEIMessages& seiMessages, SEIMessages& ne nestedSeiMessages.pop_front(); tmpMessages.push_back(sei); SEIScalableNesting *nestingSEI = new SEIScalableNesting(); +#if JVET_R0294_SUBPIC_HASH + m_seiEncoder.initSEIScalableNesting(nestingSEI, tmpMessages, targetOLSs, targetLayers, subpicIDs); +#else m_seiEncoder.initSEIScalableNesting(nestingSEI, tmpMessages, subpicIDs); +#endif seiMessages.push_back(nestingSEI); tmpMessages.clear(); } @@ -3516,6 +3524,29 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic, m_seiEncoder.initDecodedPictureHashSEI(decodedPictureHashSei, recoBuf, digestStr, pcSlice->getSPS()->getBitDepths()); trailingSeiMessages.push_back(decodedPictureHashSei); } +#if JVET_R0294_SUBPIC_HASH + // create per-subpicture decoded picture hash SEI messages, if more than one subpicture is enabled + const PPS* pps = pcPic->cs->pps; + const int numSubpics = pps->getNumSubPics(); + std::string subPicDigest; + if (numSubpics > 1 && m_pcCfg->getSubpicDecodedPictureHashType() != HASHTYPE_NONE ) + { + for (int subPicIdx = 0; subPicIdx < numSubpics; subPicIdx++) + { + const SubPic& subpic = pps->getSubPic(subPicIdx); + const UnitArea area = UnitArea(pcSlice->getSPS()->getChromaFormatIdc(), Area(subpic.getSubPicLeft(), subpic.getSubPicTop(), subpic.getSubPicWidthInLumaSample(), subpic.getSubPicHeightInLumaSample())); + PelUnitBuf recoBuf = pcPic->cs->getRecoBuf(area); + SEIDecodedPictureHash *decodedPictureHashSEI = new SEIDecodedPictureHash(); + m_seiEncoder.initDecodedPictureHashSEI(decodedPictureHashSEI, recoBuf, subPicDigest, pcSlice->getSPS()->getBitDepths()); + SEIMessages nestedSEI; + nestedSEI.push_back(decodedPictureHashSEI); + const std::vector<uint16_t> subPicIds = { (uint16_t)subpic.getSubPicID() }; + std::vector<int> targetOLS; + std::vector<int> targetLayers = {pcPic->layerId}; + xCreateScalableNestingSEI(trailingSeiMessages, nestedSEI, targetOLS, targetLayers, subPicIds); + } + } +#endif m_pcCfg->setEncodedFlag(iGOPid, true); @@ -3588,7 +3619,15 @@ void EncGOP::compressGOP( int iPOCLast, int iNumPicRcvd, PicList& rcListPic, } } } +#if JVET_R0294_SUBPIC_HASH + // Note (KJS): Using targetOLS = 0, 1 is as random as encapsulating the same SEIs in scalable nesting. + // This can just be seen as example regarding how to write scalable nesting, not what to write. + std::vector<int> targetOLS = {0, 1}; + std::vector<int> targetLayers; + xCreateScalableNestingSEI(leadingSeiMessages, nestedSeiMessages, targetOLS, targetLayers, subpicIDs); +#else xCreateScalableNestingSEI(leadingSeiMessages, nestedSeiMessages, subpicIDs); +#endif } xWriteLeadingSEIMessages( leadingSeiMessages, duInfoSeiMessages, accessUnit, pcSlice->getTLayer(), pcSlice->getSPS(), duData ); diff --git a/source/Lib/EncoderLib/EncGOP.h b/source/Lib/EncoderLib/EncGOP.h index b21293029d19598bc64c8893f0a3b49a105c44b6..d514474e3be6f20bb761dcd3a41b286ec624b3b2 100644 --- a/source/Lib/EncoderLib/EncGOP.h +++ b/source/Lib/EncoderLib/EncGOP.h @@ -307,7 +307,11 @@ protected: void xUpdateDuData(AccessUnit &testAU, std::deque<DUData> &duData); void xUpdateTimingSEI(SEIPictureTiming *pictureTimingSEI, std::deque<DUData> &duData, const SPS *sps); void xUpdateDuInfoSEI(SEIMessages &duInfoSeiMessages, SEIPictureTiming *pictureTimingSEI, int maxSubLayers); +#if JVET_R0294_SUBPIC_HASH + void xCreateScalableNestingSEI(SEIMessages& seiMessages, SEIMessages& nestedSeiMessages, const std::vector<int> &targetOLSs, const std::vector<int> &targetLayers, const std::vector<uint16_t>& subpicIDs); +#else void xCreateScalableNestingSEI(SEIMessages& seiMessages, SEIMessages& nestedSeiMessages, const std::vector<uint16_t>& subpicIDs); +#endif void xWriteSEI (NalUnitType naluType, SEIMessages& seiMessages, AccessUnit &accessUnit, AccessUnit::iterator &auPos, int temporalId); void xWriteSEISeparately (NalUnitType naluType, SEIMessages& seiMessages, AccessUnit &accessUnit, AccessUnit::iterator &auPos, int temporalId); void xClearSEIs(SEIMessages& seiMessages, bool deleteMessages); diff --git a/source/Lib/EncoderLib/SEIEncoder.cpp b/source/Lib/EncoderLib/SEIEncoder.cpp index 631021b17551eef9b159cdd35bb4fc601dbbb6ed..8ef6a69da3e559a4588bb84166ea9a28d97edd15 100644 --- a/source/Lib/EncoderLib/SEIEncoder.cpp +++ b/source/Lib/EncoderLib/SEIEncoder.cpp @@ -396,11 +396,53 @@ void SEIEncoder::initSEISampleAspectRatioInfo(SEISampleAspectRatioInfo* seiSampl //! initialize scalable nesting SEI message. //! Note: The SEI message structures input into this function will become part of the scalable nesting SEI and will be //! automatically freed, when the nesting SEI is disposed. +#if JVET_R0294_SUBPIC_HASH +// either targetOLS or targetLayer should be active, call with empty vector for the inactive mode +void SEIEncoder::initSEIScalableNesting(SEIScalableNesting *scalableNestingSEI, SEIMessages &nestedSEIs, const std::vector<int> &targetOLSs, const std::vector<int> &targetLayers, const std::vector<uint16_t> &subpictureIDs) +#else void SEIEncoder::initSEIScalableNesting(SEIScalableNesting *scalableNestingSEI, SEIMessages &nestedSEIs, const std::vector<uint16_t> &subpictureIDs) +#endif { CHECK(!(m_isInitialized), "Scalable Nesting SEI already initialized "); CHECK(!(scalableNestingSEI != NULL), "No Scalable Nesting SEI object passed"); +#if JVET_R0294_SUBPIC_HASH + CHECK (targetOLSs.size() > 0 && targetLayers.size() > 0, "Scalable Nesting SEI can apply to either OLS or layer(s), not both"); + scalableNestingSEI->m_snOlsFlag = (targetOLSs.size() > 0) ? 1 : 0; // If the nested SEI messages are picture buffering SEI messages, picture timing SEI messages or sub-picture timing SEI messages, nesting_ols_flag shall be equal to 1, by default case + if (scalableNestingSEI->m_snOlsFlag) + { + scalableNestingSEI->m_snNumOlssMinus1 = (uint32_t) targetOLSs.size() - 1; + // initialize absolute indexes + for (int i = 0; i <= scalableNestingSEI->m_snNumOlssMinus1; i++) + { + scalableNestingSEI->m_snOlsIdx[i] = targetOLSs[i]; + } + // calculate delta indexes from absolute ones + for (int i = 0; i <= scalableNestingSEI->m_snNumOlssMinus1; i++) + { + if (i == 0) + { + CHECK (scalableNestingSEI->m_snOlsIdx[i] < 0, "OLS indexes must be equal to or greater than 0"); + // no "-1" operation for the first index although the name implies one + scalableNestingSEI->m_snOlsIdxDeltaMinus1[i] = scalableNestingSEI->m_snOlsIdx[i]; + } + else + { + CHECK (scalableNestingSEI->m_snOlsIdx[i] <= scalableNestingSEI->m_snOlsIdx[i - 1], "OLS indexes must be in ascending order"); + scalableNestingSEI->m_snOlsIdxDeltaMinus1[i] = scalableNestingSEI->m_snOlsIdx[i] - scalableNestingSEI->m_snOlsIdx[i - 1] - 1; + } + } + } + else + { + scalableNestingSEI->m_snAllLayersFlag = 0; // nesting is not applied to all layers + scalableNestingSEI->m_snNumLayersMinus1 = (uint32_t) targetLayers.size() - 1; //nesting_num_layers_minus1 + for (int i=0; i <= scalableNestingSEI->m_snNumLayersMinus1; i++ ) + { + scalableNestingSEI->m_snLayerId[i] = targetLayers[i]; + } + } +#else //KJS: OLS and layer targeting needs to be fixed for the actual OLSs in the bitstream scalableNestingSEI->m_snOlsFlag = 1; // If the nested SEI messages are picture buffering SEI messages, picture timing SEI messages or sub-picture timing SEI messages, nesting_ols_flag shall be equal to 1, by default case scalableNestingSEI->m_snNumOlssMinus1 = 1; // by default the nesting scalable SEI message applies to two OLSs. @@ -423,6 +465,7 @@ void SEIEncoder::initSEIScalableNesting(SEIScalableNesting *scalableNestingSEI, scalableNestingSEI->m_snAllLayersFlag = 1; // nesting is not applied to all layers scalableNestingSEI->m_snNumLayersMinus1 = 2 - 1; //nesting_num_layers_minus1 scalableNestingSEI->m_snLayerId[0] = 0; +#endif if (!subpictureIDs.empty()) { scalableNestingSEI->m_snSubpicFlag = 1; diff --git a/source/Lib/EncoderLib/SEIEncoder.h b/source/Lib/EncoderLib/SEIEncoder.h index 2af62bb11b96afea025377c286a63e7f90009b22..bc2984c5f2e5300303b1b4108e2a5f5de47fbe04 100644 --- a/source/Lib/EncoderLib/SEIEncoder.h +++ b/source/Lib/EncoderLib/SEIEncoder.h @@ -72,8 +72,11 @@ public: #if U0033_ALTERNATIVE_TRANSFER_CHARACTERISTICS_SEI void initSEIAlternativeTransferCharacteristics(SEIAlternativeTransferCharacteristics *sei); #endif +#if JVET_R0294_SUBPIC_HASH + void initSEIScalableNesting(SEIScalableNesting *scalableNestingSEI, SEIMessages &nestedSEIs, const std::vector<int> &targetOLSs, const std::vector<int> &targetLayers, const std::vector<uint16_t> &subpictureIDs); +#else void initSEIScalableNesting(SEIScalableNesting *scalableNestingSEI, SEIMessages &nestedSEIs, const std::vector<uint16_t> &subpictureIDs); - // trailing SEIs +#endif void initDecodedPictureHashSEI(SEIDecodedPictureHash *sei, PelUnitBuf& pic, std::string &rHashString, const BitDepths &bitDepths); void initSEIErp(SEIEquirectangularProjection *sei); void initSEISphereRotation(SEISphereRotation *sei);