/* The copyright in this software is being made available under the BSD * License, included below. This software may be subject to other third party * and contributor rights, including patent rights, and no such rights are * granted under this license. * * Copyright (c) 2010-2023, ITU/ISO/IEC * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the ITU/ISO/IEC nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include "CommonLib/CommonDef.h" #include "BitstreamExtractorApp.h" #include "DecoderLib/AnnexBread.h" #include "DecoderLib/NALread.h" #include "EncoderLib/NALwrite.h" #include "DecoderLib/VLCReader.h" #include "EncoderLib/VLCWriter.h" #include "EncoderLib/AnnexBwrite.h" BitstreamExtractorApp::BitstreamExtractorApp() :m_vpsId(-1) , m_removeTimingSEI (false) { } void BitstreamExtractorApp::xPrintVPSInfo (VPS *vps) { msg (VERBOSE, "VPS Info: \n"); msg (VERBOSE, " VPS ID : %d\n", vps->getVPSId()); msg (VERBOSE, " Max layers : %d\n", vps->getMaxLayers()); msg (VERBOSE, " Max sub-layers : %d\n", vps->getMaxSubLayers()); msg (VERBOSE, " Number of OLS : %d\n", vps->getTotalNumOLSs()); for (int olsIdx=0; olsIdx < vps->getTotalNumOLSs(); olsIdx++) { vps->deriveTargetOutputLayerSet(olsIdx); msg (VERBOSE, " OLS # %d\n", olsIdx); msg (VERBOSE, " Output layers: "); for( int i = 0; i < vps->m_targetOutputLayerIdSet.size(); i++ ) { msg (VERBOSE, "%d ", vps->m_targetOutputLayerIdSet[i]); } msg (VERBOSE, "\n"); msg (VERBOSE, " Target layers: "); for( int i = 0; i < vps->m_targetLayerIdSet.size(); i++ ) { msg (VERBOSE, "%d ", vps->m_targetLayerIdSet[i]); } msg (VERBOSE, "\n"); } } void BitstreamExtractorApp::xPrintSubPicInfo (PPS *pps) { msg (VERBOSE, "Subpic Info: \n"); msg (VERBOSE, " SPS ID : %d\n", pps->getSPSId()); msg (VERBOSE, " PPS ID : %d\n", pps->getPPSId()); msg (VERBOSE, " Subpic enabled : %s\n", pps->getNumSubPics() > 1 ? "yes": "no" ); if ( pps->getNumSubPics() > 1) { msg (VERBOSE, " Number of subpics : %d\n", pps->getNumSubPics() ); for (int i=0; igetNumSubPics(); i++) { SubPic subP = pps->getSubPic(i); msg ( VERBOSE, " SubpicIdx #%d : TL=(%d, %d) Size CTU=(%d, %d) Size Pel=(%d, %d) SubpicID=%d\n", i, subP.getSubPicCtuTopLeftX(), subP.getSubPicCtuTopLeftY(), subP.getSubPicWidthInCTUs(), subP.getSubPicHeightInCTUs(), subP.getSubPicWidthInLumaSample(), subP.getSubPicHeightInLumaSample(), subP.getSubPicID()); } } } void BitstreamExtractorApp::xReadPicHeader(InputNALUnit &nalu) { m_hlSynaxReader.setBitstream(&nalu.getBitstream()); m_hlSynaxReader.parsePictureHeader(&m_picHeader, &m_parameterSetManager, true); m_picHeader.setValid(); } Slice BitstreamExtractorApp::xParseSliceHeader(InputNALUnit &nalu) { m_hlSynaxReader.setBitstream(&nalu.getBitstream()); Slice slice; slice.initSlice(); slice.setNalUnitType(nalu.m_nalUnitType); slice.setNalUnitLayerId(nalu.m_nuhLayerId); slice.setTLayer(nalu.m_temporalId); m_hlSynaxReader.parseSliceHeader(&slice, &m_picHeader, &m_parameterSetManager, m_prevTid0Poc, m_prevPicPOC); return slice; } bool BitstreamExtractorApp::xCheckSliceSubpicture(Slice &slice, int targetSubPicId) { PPS *pps = m_parameterSetManager.getPPS(m_picHeader.getPPSId()); CHECK(nullptr == pps, "referenced PPS not found"); SPS *sps = m_parameterSetManager.getSPS(pps->getSPSId()); CHECK(nullptr == sps, "referenced SPS not found"); if (sps->getSubPicInfoPresentFlag()) { // subpic ID is explicitly indicated msg(VERBOSE, "found slice subpic id %d\n", slice.getSliceSubPicId()); return (targetSubPicId == slice.getSliceSubPicId()); } else { THROW("Subpicture signalling disbled, cannot extract."); } return true; } bool BitstreamExtractorApp::xCheckSEIFiller(SEIMessages SEIs, int targetSubPicId, bool &rmAllFillerInSubpicExt, bool lastSliceWritten) { for (auto sei : SEIs) { if (sei->payloadType() == SEI::PayloadType::SUBPICTURE_LEVEL_INFO) { SEISubpicureLevelInfo *seiSLI = (SEISubpicureLevelInfo *)sei; if (!seiSLI->m_cbrConstraintFlag) { rmAllFillerInSubpicExt = true; } } } for (auto sei : SEIs) { if (sei->payloadType() == SEI::PayloadType::FILLER_PAYLOAD) { return (rmAllFillerInSubpicExt ? false : lastSliceWritten); } } return true; } void BitstreamExtractorApp::xRewriteSPS (SPS &targetSPS, const SPS &sourceSPS, SubPic &subPic) { targetSPS = sourceSPS; // set the number of subpicture to 1, location should not be transmited targetSPS.setNumSubPics(1); // set the target subpicture ID as first ID targetSPS.setSubPicIdMappingExplicitlySignalledFlag(true); targetSPS.setSubPicIdMappingPresentFlag(true); targetSPS.setSubPicId(0, subPic.getSubPicID()); targetSPS.setMaxPicWidthInLumaSamples(subPic.getSubPicWidthInLumaSample()); targetSPS.setMaxPicHeightInLumaSamples(subPic.getSubPicHeightInLumaSample()); // Set the new conformance window Window& conf = targetSPS.getConformanceWindow(); int subpicConfWinLeftOffset = (subPic.getSubPicCtuTopLeftX() == 0) ? conf.getWindowLeftOffset() : 0; int subpicConfWinRightOffset = ((subPic.getSubPicCtuTopLeftX() + subPic.getSubPicWidthInCTUs()) * sourceSPS.getCTUSize() >= sourceSPS.getMaxPicWidthInLumaSamples()) ? conf.getWindowRightOffset() : 0; int subpicConfWinTopOffset = (subPic.getSubPicCtuTopLeftY() == 0) ? conf.getWindowTopOffset() : 0; int subpicConfWinBottomOffset = ((subPic.getSubPicCtuTopLeftY() + subPic.getSubPicHeightInCTUs()) * sourceSPS.getCTUSize() >= sourceSPS.getMaxPicHeightInLumaSamples()) ? conf.getWindowBottomOffset() : 0; conf.setWindowLeftOffset(subpicConfWinLeftOffset); conf.setWindowRightOffset(subpicConfWinRightOffset); conf.setWindowTopOffset(subpicConfWinTopOffset); conf.setWindowBottomOffset(subpicConfWinBottomOffset); if (sourceSPS.getVirtualBoundariesEnabledFlag() && sourceSPS.getVirtualBoundariesPresentFlag()) { targetSPS.setNumVerVirtualBoundaries(0); for (int i = 0; i < sourceSPS.getNumVerVirtualBoundaries() ; i ++) { int subPicLeftX = subPic.getSubPicCtuTopLeftX() * sourceSPS.getCTUSize(); int subPicRightX = (subPic.getSubPicCtuTopLeftX() + subPic.getSubPicWidthInCTUs()) * sourceSPS.getCTUSize(); if (subPicRightX > sourceSPS.getMaxPicWidthInLumaSamples()) { subPicRightX = sourceSPS.getMaxPicWidthInLumaSamples(); } if ( sourceSPS.getVirtualBoundariesPosX(i) > subPicLeftX && sourceSPS.getVirtualBoundariesPosX(i) < subPicRightX) { targetSPS.setVirtualBoundariesPosX(targetSPS.getNumVerVirtualBoundaries(), sourceSPS.getVirtualBoundariesPosX(i) - subPicLeftX); targetSPS.setNumVerVirtualBoundaries(targetSPS.getNumVerVirtualBoundaries() + 1); } } targetSPS.setNumHorVirtualBoundaries(0); for (int i = 0; i < sourceSPS.getNumHorVirtualBoundaries(); i++) { int subPicTopY = subPic.getSubPicCtuTopLeftY() * sourceSPS.getCTUSize(); int subPicBottomY = (subPic.getSubPicCtuTopLeftY() + subPic.getSubPicHeightInCTUs()) * sourceSPS.getCTUSize(); if (subPicBottomY > sourceSPS.getMaxPicHeightInLumaSamples()) { subPicBottomY = sourceSPS.getMaxPicHeightInLumaSamples(); } if (sourceSPS.getVirtualBoundariesPosY(i) > subPicTopY && sourceSPS.getVirtualBoundariesPosY(i) < subPicBottomY) { targetSPS.setVirtualBoundariesPosY(targetSPS.getNumHorVirtualBoundaries(), sourceSPS.getVirtualBoundariesPosY(i) - subPicTopY); targetSPS.setNumHorVirtualBoundaries(targetSPS.getNumHorVirtualBoundaries() + 1); } } if (targetSPS.getNumVerVirtualBoundaries() == 0 && targetSPS.getNumHorVirtualBoundaries() == 0) { targetSPS.setVirtualBoundariesEnabledFlag(0); } } } void BitstreamExtractorApp::xRewritePPS(PPS &targetPPS, const PPS &sourcePPS, const SPS &sourceSPS, SubPic &subPic) { targetPPS = sourcePPS; // set number of subpictures to 1 targetPPS.setNumSubPics(1); // set taget subpicture ID as first ID targetPPS.setSubPicId(0, subPic.getSubPicID()); // we send the ID in the SPS, so don't sent it in the PPS (hard coded decision) targetPPS.setSubPicIdMappingInPpsFlag(false); // picture size targetPPS.setPicWidthInLumaSamples(subPic.getSubPicWidthInLumaSample()); targetPPS.setPicHeightInLumaSamples(subPic.getSubPicHeightInLumaSample()); // todo: Conformance window (conf window rewriting is not needed per JVET-S0117) if (sourcePPS.getScalingWindow().getWindowEnabledFlag()) { int subWidthC = SPS::getWinUnitX(sourceSPS.getChromaFormatIdc()); int subHeightC = SPS::getWinUnitY(sourceSPS.getChromaFormatIdc()); int subpicScalWinLeftOffset = sourcePPS.getScalingWindow().getWindowLeftOffset() - (int)subPic.getSubPicCtuTopLeftX() * sourceSPS.getCTUSize() / subWidthC; int rightSubpicBd = (subPic.getSubPicCtuTopLeftX() + subPic.getSubPicWidthInCTUs()) * sourceSPS.getCTUSize(); int subpicScalWinRightOffset = rightSubpicBd >= sourceSPS.getMaxPicWidthInLumaSamples() ? sourcePPS.getScalingWindow().getWindowRightOffset() : sourcePPS.getScalingWindow().getWindowRightOffset() - (int)(sourceSPS.getMaxPicWidthInLumaSamples() - rightSubpicBd) / subWidthC; int subpicScalWinTopOffset = sourcePPS.getScalingWindow().getWindowTopOffset() - (int)subPic.getSubPicCtuTopLeftY() * sourceSPS.getCTUSize() / subHeightC; int botSubpicBd = (subPic.getSubPicCtuTopLeftY() + subPic.getSubPicHeightInCTUs()) * sourceSPS.getCTUSize(); int subpicScalWinBotOffset = botSubpicBd >= sourceSPS.getMaxPicHeightInLumaSamples() ? sourcePPS.getScalingWindow().getWindowBottomOffset() : sourcePPS.getScalingWindow().getWindowBottomOffset() - (int)(sourceSPS.getMaxPicHeightInLumaSamples() - botSubpicBd) / subHeightC; Window scalingWindow; scalingWindow.setWindow(subpicScalWinLeftOffset, subpicScalWinRightOffset, subpicScalWinTopOffset, subpicScalWinBotOffset); targetPPS.setScalingWindow(scalingWindow); } // Tiles int numTileCols = 1; int numTileRows = 1; std::vector tileColWidth; std::vector tileRowHeight; std::vector tileColBd; std::vector tileRowBd; int subpicTopLeftTileX = -1; int subpicTopLeftTileY = -1; for (int i=0; i<= sourcePPS.getNumTileColumns(); i++) { const int currentColBd = sourcePPS.getTileColumnBd(i); if ((currentColBd >= subPic.getSubPicCtuTopLeftX()) && (currentColBd <= (subPic.getSubPicCtuTopLeftX() + subPic.getSubPicWidthInCTUs()))) { tileColBd.push_back(currentColBd - subPic.getSubPicCtuTopLeftX()); if (subpicTopLeftTileX == -1) { subpicTopLeftTileX = i; } } } numTileCols=(int)tileColBd.size() - 1; CHECK (numTileCols < 1, "After extraction there should be at least one tile horizonally."); tileColWidth.resize(numTileCols); for (int i=0; i= subPic.getSubPicCtuTopLeftY()) && (currentRowBd <= (subPic.getSubPicCtuTopLeftY() + subPic.getSubPicHeightInCTUs()))) { tileRowBd.push_back(currentRowBd - subPic.getSubPicCtuTopLeftY()); if(subpicTopLeftTileY == -1) { subpicTopLeftTileY = i; } } } numTileRows=(int)tileRowBd.size() - 1; // if subpicture was part of a tile, top and/or bottom borders need to be added // note: this can only happen with vertical slice splits of a tile if (numTileRows < 1 ) { if (tileRowBd.size()==0) { tileRowBd.push_back(0); tileRowBd.push_back(subPic.getSubPicHeightInCTUs()); numTileRows+=2; } else { if (tileRowBd[0] == 0) { // top border exists, add bottom tileRowBd.push_back(subPic.getSubPicHeightInCTUs()); numTileRows++; } else { // bottom border exists, add top const int row1 = tileRowBd[0]; tileRowBd[0] = 0; tileRowBd.push_back(row1); numTileRows++; } } } tileRowHeight.resize(numTileRows); for (int i=0; i= vps->getNumSubLayersInLayerInOLS(m_targetOlsIdx, vps->getGeneralLayerIdx(nalu.m_nuhLayerId)); return retval; } bool BitstreamExtractorApp::xCheckSEIsSubPicture(SEIMessages& SEIs, InputNALUnit& nalu, std::ostream& out, int subpicId, VPS *vps) { SEIMessages scalableNestingSEIs = getSeisByType(SEIs, SEI::PayloadType::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(), subpicId) != sei->m_snSubpicId.end()) { // C.7 step 7.c if (sei->m_snOlsFlag || vps->getNumLayersInOls(m_targetOlsIdx) == 1) { // 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::PayloadType::DECODED_PICTURE_HASH); if (hashSEI.size() > 0) { return false; } // keep all other SEIs return true; } bool BitstreamExtractorApp::xIsTargetOlsIncludeAllVclLayers() { std::ifstream bitstreamFileIn(m_bitstreamFileNameIn.c_str(), std::ifstream::in | std::ifstream::binary); if (!bitstreamFileIn) { EXIT("failed to open bitstream file " << m_bitstreamFileNameIn.c_str() << " for reading"); } InputByteStream bytestream(bitstreamFileIn); bitstreamFileIn.clear(); bitstreamFileIn.seekg(0, std::ios::beg); if (m_targetOlsIdx >= 0) { std::vector layerIdInTargetOls; std::vector layerIdInVclNalu; while (!!bitstreamFileIn) { AnnexBStats stats = AnnexBStats(); InputNALUnit nalu; byteStreamNALUnit(bytestream, nalu.getBitstream().getFifo(), stats); // call actual decoding function if (nalu.getBitstream().getFifo().empty()) { msg(WARNING, "Warning: Attempt to decode an empty NAL unit"); } else { read(nalu); if (nalu.m_nalUnitType == NAL_UNIT_VPS) { VPS* vps = new VPS(); m_hlSynaxReader.setBitstream(&nalu.getBitstream()); m_hlSynaxReader.parseVPS(vps); int vpsId = vps->getVPSId(); // note: storeVPS may invalidate the vps pointer! m_parameterSetManager.storeVPS(vps, nalu.getBitstream().getFifo()); // get VPS back vps = m_parameterSetManager.getVPS(vpsId); m_vpsId = vps->getVPSId(); } VPS *vps = nullptr; if (m_vpsId > 0) { vps = m_parameterSetManager.getVPS(m_vpsId); layerIdInTargetOls = vps->getLayerIdsInOls(m_targetOlsIdx); if (NALUnit::isVclNalUnitType(nalu.m_nalUnitType)) { if (layerIdInVclNalu.size() == 0 || nalu.m_nuhLayerId >= layerIdInVclNalu[layerIdInVclNalu.size() - 1]) { layerIdInVclNalu.push_back(nalu.m_nuhLayerId); } else { break; } } } } } //When LayerIdInOls[ targetOlsIdx ] does not include all values of nuh_layer_id in all VCL NAL units in the bitstream inBitstream for (int i = 0; i < layerIdInVclNalu.size(); i++) { bool vclLayerIncludedInTargetOls = std::find(layerIdInTargetOls.begin(), layerIdInTargetOls.end(), layerIdInVclNalu[i]) != layerIdInTargetOls.end(); if (!vclLayerIncludedInTargetOls) { return false; } } } return true; } uint32_t BitstreamExtractorApp::decode() { std::ifstream bitstreamFileIn(m_bitstreamFileNameIn.c_str(), std::ifstream::in | std::ifstream::binary); if (!bitstreamFileIn) { EXIT( "failed to open bitstream file " << m_bitstreamFileNameIn.c_str() << " for reading" ) ; } std::ofstream bitstreamFileOut(m_bitstreamFileNameOut.c_str(), std::ifstream::out | std::ifstream::binary); InputByteStream bytestream(bitstreamFileIn); bitstreamFileIn.clear(); bitstreamFileIn.seekg( 0, std::ios::beg ); int unitCnt = 0; bool lastSliceWritten= false; // stores status of previous slice for associated filler data NAL units VPS *vpsIdZero = new VPS(); std::vector empty; m_parameterSetManager.storeVPS(vpsIdZero, empty); int subpicIdTarget[MAX_VPS_LAYERS]; for (int i = 0; i < MAX_VPS_LAYERS; i++) { subpicIdTarget[i] = -1; } bool isVclNalUnitRemoved[MAX_VPS_LAYERS] = { false }; bool isMultiSubpicLayer[MAX_VPS_LAYERS] = { false }; bool rmAllFillerInSubpicExt[MAX_VPS_LAYERS] = { false }; bool targetOlsIncludeAllVclLayers = xIsTargetOlsIncludeAllVclLayers(); while (!!bitstreamFileIn) { AnnexBStats stats = AnnexBStats(); InputNALUnit nalu; byteStreamNALUnit(bytestream, nalu.getBitstream().getFifo(), stats); // call actual decoding function if (nalu.getBitstream().getFifo().empty()) { /* this can happen if the following occur: * - empty input file * - two back-to-back start_code_prefixes * - start_code_prefix immediately followed by EOF */ msg(WARNING, "Warning: Attempt to decode an empty NAL unit" ); } else { read(nalu); bool writeInpuNalUnitToStream = true; // Remove NAL units with TemporalId greater than tIdTarget. writeInpuNalUnitToStream &= ( m_maxTemporalLayer < 0 ) || ( nalu.m_temporalId <= m_maxTemporalLayer ); if( nalu.m_nalUnitType == NAL_UNIT_VPS ) { VPS* vps = new VPS(); m_hlSynaxReader.setBitstream( &nalu.getBitstream() ); m_hlSynaxReader.parseVPS( vps ); int vpsId = vps->getVPSId(); // note: storeVPS may invalidate the vps pointer! m_parameterSetManager.storeVPS( vps, nalu.getBitstream().getFifo() ); // get VPS back vps = m_parameterSetManager.getVPS(vpsId); xPrintVPSInfo(vps); m_vpsId = vps->getVPSId(); // example: just write the parsed VPS back to the stream // *** add modifications here *** // only write, if not dropped earlier if (writeInpuNalUnitToStream) { xWriteVPS(vps, bitstreamFileOut, nalu.m_nuhLayerId, nalu.m_temporalId); writeInpuNalUnitToStream = false; } } VPS *vps = nullptr; bool isIncludedInTargetOls = true; if (m_targetOlsIdx >= 0 && m_vpsId >=0 ) { // if there is no VPS nal unit, there shall be one OLS and one layer. if (m_vpsId == 0) { CHECK(m_targetOlsIdx != 0, "only one OLS and one layer exist, but target olsIdx is not equal to zero"); } // Remove NAL units with nal_unit_type not equal to any of VPS_NUT, DPS_NUT, and EOB_NUT and with nuh_layer_id not included in the list LayerIdInOls[targetOlsIdx]. NalUnitType t = nalu.m_nalUnitType; // remove from outBitstream all NAL units that have nuh_layer_id not included in the list LayerIdInOls[ targetOlsIdx ] and are not DCI, OPI, VPS, AUD, EOB or SEI NAL units bool isSpecialNalTypes = t == NAL_UNIT_OPI || t == NAL_UNIT_VPS || t == NAL_UNIT_DCI || t == NAL_UNIT_EOB || t == NAL_UNIT_PREFIX_SEI || t == NAL_UNIT_SUFFIX_SEI; vps = m_parameterSetManager.getVPS(m_vpsId); if (m_vpsId == 0) { // this initialization can be removed once the dummy VPS is properly initialized in the parameter set manager vps->deriveOutputLayerSets(); } uint32_t numOlss = vps->getTotalNumOLSs(); CHECK(m_targetOlsIdx <0 || m_targetOlsIdx >= numOlss, "target OLS shall be in the range of OLSs specified by the VPS"); CHECK(m_maxTemporalLayer < -1 || m_maxTemporalLayer > (int)vps->getPtlMaxTemporalId(vps->getOlsPtlIdx(m_targetOlsIdx)), "MaxTemporalLayer shall either be equal -1 (for disabled) or in the range of 0 to vps_ptl_max_tid[ vps_ols_ptl_idx[ targetOlsIdx ] ], inclusive"); std::vector layerIdInOls = vps->getLayerIdsInOls(m_targetOlsIdx); isIncludedInTargetOls = std::find(layerIdInOls.begin(), layerIdInOls.end(), nalu.m_nuhLayerId) != layerIdInOls.end(); writeInpuNalUnitToStream &= (isSpecialNalTypes || isIncludedInTargetOls); writeInpuNalUnitToStream &= !xCheckNumSubLayers(nalu, vps); m_removeTimingSEI = !vps->getGeneralHrdParameters()->getGeneralSamePicTimingInAllOlsFlag(); } if( nalu.m_nalUnitType == NAL_UNIT_SPS ) { SPS* sps = new SPS(); m_hlSynaxReader.setBitstream( &nalu.getBitstream() ); m_hlSynaxReader.parseSPS( sps ); int spsId = sps->getSPSId(); // note: storeSPS may invalidate the sps pointer! m_parameterSetManager.storeSPS( sps, nalu.getBitstream().getFifo() ); // get SPS back sps = m_parameterSetManager.getSPS(spsId); msg (VERBOSE, "SPS Info: SPS ID = %d\n", spsId); // example: just write the parsed SPS back to the stream // *** add modifications here *** // only write, if not dropped earlier // rewrite the SPS isMultiSubpicLayer[nalu.m_nuhLayerId] = sps->getNumSubPics() > 1 ? true : false; if (isMultiSubpicLayer[nalu.m_nuhLayerId]) { subpicIdTarget[nalu.m_nuhLayerId] = 0; } if (m_subPicIdx >= 0 && isMultiSubpicLayer[nalu.m_nuhLayerId]) { CHECK(m_subPicIdx >= sps->getNumSubPics(), "Target subpicture not found"); CHECK(!sps->getSubPicTreatedAsPicFlag(m_subPicIdx), "sps_subpic_treated_as_pic_flag[subpicIdxTarget] should be equal to 1 for subpicture extraction"); xSetSPSUpdated(sps->getSPSId()); writeInpuNalUnitToStream = false; } if (writeInpuNalUnitToStream) { xWriteSPS(sps, bitstreamFileOut, nalu.m_nuhLayerId, nalu.m_temporalId); writeInpuNalUnitToStream = false; } } if( nalu.m_nalUnitType == NAL_UNIT_PPS ) { PPS* pps = new PPS(); m_hlSynaxReader.setBitstream( &nalu.getBitstream() ); m_hlSynaxReader.parsePPS( pps ); int ppsId = pps->getPPSId(); // note: storePPS may invalidate the pps pointer! m_parameterSetManager.storePPS( pps, nalu.getBitstream().getFifo() ); // get PPS back pps = m_parameterSetManager.getPPS(ppsId); msg (VERBOSE, "PPS Info: PPS ID = %d\n", pps->getPPSId()); SPS *sps = m_parameterSetManager.getSPS(pps->getSPSId()); if ( nullptr == sps) { printf("Cannot find SPS referred to by PPS, ignoring"); } else { if( pps->getNoPicPartitionFlag() ) { pps->resetTileSliceInfo(); pps->setLog2CtuSize( ceilLog2(sps->getCTUSize()) ); pps->setNumExpTileColumns(1); pps->setNumExpTileRows(1); pps->addTileColumnWidth( pps->getPicWidthInCtu( ) ); pps->addTileRowHeight( pps->getPicHeightInCtu( ) ); pps->initTiles(); pps->setRectSliceFlag( 1 ); pps->setNumSlicesInPic( 1 ); pps->initRectSlices( ); pps->setTileIdxDeltaPresentFlag( 0 ); pps->setSliceTileIdx( 0, 0 ); } pps->initRectSliceMap(sps); pps->initSubPic(*sps); xPrintSubPicInfo (pps); if (m_subPicIdx >= 0 && isMultiSubpicLayer[nalu.m_nuhLayerId] && writeInpuNalUnitToStream) { SubPic subPic; subPic = pps->getSubPic(m_subPicIdx); subpicIdTarget[nalu.m_nuhLayerId] = subPic.getSubPicID(); // if the referred SPS was updated, modify and write it if (xIsSPSUpdate(sps->getSPSId())) { SPS targetSPS; xRewriteSPS(targetSPS, *sps, subPic); xWriteSPS(&targetSPS, bitstreamFileOut, nalu.m_nuhLayerId, nalu.m_temporalId); xClearSPSUpdated(sps->getSPSId()); } // rewrite the PPS PPS targetPPS; xRewritePPS(targetPPS, *pps, *sps, subPic); xWritePPS(&targetPPS, bitstreamFileOut, nalu.m_nuhLayerId, nalu.m_temporalId); writeInpuNalUnitToStream = false; } } // example: just write the parsed PPS back to the stream // *** add modifications here *** // only write, if not dropped earlier if (writeInpuNalUnitToStream) { xWritePPS(pps, bitstreamFileOut, nalu.m_nuhLayerId, nalu.m_temporalId); writeInpuNalUnitToStream = false; } } // when re-using code for slice header parsing, we need to store APSs if( ( nalu.m_nalUnitType == NAL_UNIT_PREFIX_APS ) || ( nalu.m_nalUnitType == NAL_UNIT_SUFFIX_APS )) { APS* aps = new APS(); m_hlSynaxReader.setBitstream( &nalu.getBitstream() ); m_hlSynaxReader.parseAPS( aps ); msg (VERBOSE, "APS Info: APS ID = %d Type = %d Layer = %d\n", aps->getAPSId(), aps->getAPSType(), nalu.m_nuhLayerId); int apsId = aps->getAPSId(); ApsType apsType = aps->getAPSType(); // note: storeAPS may invalidate the aps pointer! m_parameterSetManager.storeAPS(aps, nalu.getBitstream().getFifo()); // get APS back aps = m_parameterSetManager.getAPS(apsId, apsType); } if (nalu.m_nalUnitType == NAL_UNIT_PH) { xReadPicHeader(nalu); } if ( (nalu.m_nalUnitType == NAL_UNIT_PREFIX_SEI) || (nalu.m_nalUnitType == NAL_UNIT_SUFFIX_SEI)) { // decode SEI SEIMessages SEIs; 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) { for (auto sei : SEIs) { // remove from outBitstream all NAL units that have nuh_layer_id not included in the list LayerIdInOls[ targetOlsIdx ] and ( are SEI NAL units containing (scalable-nested SEI messages) or (non-scalable-nested SEI messages with PayloadType not equal to 0, 1, 130, or 203) ) bool isNonNestedHRDSEI = false; if (sei->payloadType() == SEI::PayloadType::BUFFERING_PERIOD || sei->payloadType() == SEI::PayloadType::PICTURE_TIMING || sei->payloadType() == SEI::PayloadType::DECODING_UNIT_INFO || sei->payloadType() == SEI::PayloadType::SUBPICTURE_LEVEL_INFO) { isNonNestedHRDSEI = true; } writeInpuNalUnitToStream &= isIncludedInTargetOls || (sei->payloadType() != SEI::PayloadType::SCALABLE_NESTING && isNonNestedHRDSEI); // remove unqualified scalable nesting SEI if (sei->payloadType() == SEI::PayloadType::SCALABLE_NESTING) { SEIScalableNesting *seiNesting = (SEIScalableNesting *)sei; if (seiNesting->m_snOlsFlag == 1) { bool targetOlsIdxInNestingAppliedOls = false; for (uint32_t i = 0; i <= seiNesting->m_snNumOlssMinus1; i++) { if (seiNesting->m_snOlsIdx[i] == m_targetOlsIdx) { targetOlsIdxInNestingAppliedOls = true; break; } } writeInpuNalUnitToStream &= targetOlsIdxInNestingAppliedOls; } // C.6 step 9.c if (writeInpuNalUnitToStream && !targetOlsIncludeAllVclLayers && !seiNesting->m_snSubpicFlag) { if (seiNesting->m_snOlsFlag || vps->getNumLayersInOls(m_targetOlsIdx) == 1) { OutputNALUnit outNalu(nalu.m_nalUnitType, nalu.m_nuhLayerId, nalu.m_temporalId); m_seiWriter.writeSEImessages(outNalu.m_bitstream, seiNesting->m_nestedSEIs, m_hrd, false, nalu.m_temporalId); NALUnitEBSP naluWithHeader(outNalu); writeAnnexBNalUnit(bitstreamFileOut, naluWithHeader, true); writeInpuNalUnitToStream = false; } } } // remove unqualified timing related SEI if (sei->payloadType() == SEI::PayloadType::BUFFERING_PERIOD || (m_removeTimingSEI && sei->payloadType() == SEI::PayloadType::PICTURE_TIMING) || sei->payloadType() == SEI::PayloadType::DECODING_UNIT_INFO || sei->payloadType() == SEI::PayloadType::SUBPICTURE_LEVEL_INFO) { writeInpuNalUnitToStream &= targetOlsIncludeAllVclLayers; } } if (m_vpsId == -1) { delete vps; } } writeInpuNalUnitToStream &= xCheckSEIFiller(SEIs, subpicIdTarget[nalu.m_nuhLayerId], rmAllFillerInSubpicExt[nalu.m_nuhLayerId], lastSliceWritten); if (writeInpuNalUnitToStream && isVclNalUnitRemoved[nalu.m_nuhLayerId] && m_subPicIdx >= 0) { writeInpuNalUnitToStream &= xCheckSEIsSubPicture(SEIs, nalu, bitstreamFileOut, subpicIdTarget[nalu.m_nuhLayerId], vps); } } Slice slice; if (nalu.isSlice()) { slice = xParseSliceHeader(nalu); } if (isMultiSubpicLayer[nalu.m_nuhLayerId] && writeInpuNalUnitToStream) { if (m_subPicIdx >= 0 && nalu.isSlice()) { writeInpuNalUnitToStream = xCheckSliceSubpicture(slice, subpicIdTarget[nalu.m_nuhLayerId]); if (!writeInpuNalUnitToStream) { isVclNalUnitRemoved[nalu.m_nuhLayerId] = true; } } if (nalu.m_nalUnitType == NAL_UNIT_FD) { writeInpuNalUnitToStream = rmAllFillerInSubpicExt[nalu.m_nuhLayerId] ? false : lastSliceWritten; } } if (nalu.isSlice() && writeInpuNalUnitToStream) { m_prevPicPOC = slice.getPOC(); } unitCnt++; if( writeInpuNalUnitToStream ) { int numZeros = stats.m_numLeadingZero8BitsBytes + stats.m_numZeroByteBytes + stats.m_numStartCodePrefixBytes -1; // write start code char ch = 0; for( int i = 0 ; i < numZeros; i++ ) { bitstreamFileOut.write( &ch, 1 ); } ch = 1; bitstreamFileOut.write( &ch, 1 ); // create output NAL unit OutputNALUnit out (nalu.m_nalUnitType, nalu.m_nuhLayerId, nalu.m_temporalId); out.m_bitstream.getFifo() = nalu.getBitstream().getFifo(); // write with start code emulation prevention writeNaluContent (bitstreamFileOut, out); } // update status of previous slice if (nalu.isSlice()) { if (writeInpuNalUnitToStream) { lastSliceWritten = true; } else { lastSliceWritten=false; } } } } return 0; }