diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b9737cc9885c4bda03c5cd1bced0182d9d3d0d3..bfd765376da7ba8eae364b2be82c3d84237875da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -146,6 +146,7 @@ add_subdirectory( "source/App/DecoderApp" ) add_subdirectory( "source/App/EncoderApp" ) add_subdirectory( "source/App/SEIRemovalApp" ) add_subdirectory( "source/App/Parcat" ) +add_subdirectory( "source/App/StreamMergeApp" ) if( EXTENSION_360_VIDEO ) add_subdirectory( "source/App/utils/360ConvertApp" ) endif() diff --git a/Makefile b/Makefile index d61744bed9a7926118b9107c9e59384d12a3126d..c48915825cb53879040adfad525f555ed0770dbf 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ BUILD_SCRIPT := $(CURDIR)/cmake/CMakeBuild/bin/cmake.py # TARGETS := CommonLib DecoderAnalyserApp DecoderAnalyserLib DecoderApp DecoderLib -TARGETS += EncoderApp EncoderLib Utilities SEIRemovalApp +TARGETS += EncoderApp EncoderLib Utilities SEIRemovalApp StreamMergeApp ifeq ($(OS),Windows_NT) ifneq ($(MSYSTEM),) diff --git a/source/App/DecoderApp/DecApp.cpp b/source/App/DecoderApp/DecApp.cpp index ea6b2fc8a3c99a432ba72558824a1410a9fd2782..c3b35b5ea53e783a89d922dce66d0acdad0e0e90 100644 --- a/source/App/DecoderApp/DecApp.cpp +++ b/source/App/DecoderApp/DecApp.cpp @@ -153,7 +153,11 @@ uint32_t DecApp::decode() { read(nalu); +#if JVET_N0278_HLS + if ((m_iMaxTemporalLayer >= 0 && nalu.m_temporalId > m_iMaxTemporalLayer) || !isNaluWithinTargetDecLayerIdSet(&nalu) || !isNaluTheTargetLayer(&nalu)) +#else if( (m_iMaxTemporalLayer >= 0 && nalu.m_temporalId > m_iMaxTemporalLayer) || !isNaluWithinTargetDecLayerIdSet(&nalu) ) +#endif { bNewPicture = false; } @@ -308,6 +312,11 @@ void DecApp::xCreateDecLib() #endif ); m_cDecLib.setDecodedPictureHashSEIEnabled(m_decodedPictureHashSEIEnabled); + +#if JVET_N0278_HLS + m_cDecLib.setTargetDecLayer(m_iTargetLayer); +#endif + if (!m_outputDecodedSEIMessagesFilename.empty()) { std::ostream &os=m_seiMessageFileStream.is_open() ? m_seiMessageFileStream : std::cout; @@ -679,4 +688,16 @@ bool DecApp::isNaluWithinTargetDecLayerIdSet( InputNALUnit* nalu ) return false; } +#if JVET_N0278_HLS +/** \param nalu Input nalu to check whether its LayerId is the specified target layer +*/ +bool DecApp::isNaluTheTargetLayer(InputNALUnit* nalu) +{ + if (nalu->m_nuhLayerId == m_iTargetLayer || m_iTargetLayer < 0) + return true; + + return false; +} +#endif + //! \} diff --git a/source/App/DecoderApp/DecApp.h b/source/App/DecoderApp/DecApp.h index a0bf8a6fb4b48fdca0c477bf5ffd74738a64fa08..5ef5e735b54893d6b4f43b8ad72fde95ad209b51 100644 --- a/source/App/DecoderApp/DecApp.h +++ b/source/App/DecoderApp/DecApp.h @@ -81,6 +81,9 @@ private: void xWriteOutput ( PicList* pcListPic , uint32_t tId); ///< write YUV to file void xFlushOutput ( PicList* pcListPic ); ///< flush all remaining decoded pictures to file bool isNaluWithinTargetDecLayerIdSet ( InputNALUnit* nalu ); ///< check whether given Nalu is within targetDecLayerIdSet +#if JVET_N0278_HLS + bool isNaluTheTargetLayer(InputNALUnit* nalu); ///< check whether given Nalu is within targetDecLayerIdSet +#endif }; //! \} diff --git a/source/App/DecoderApp/DecAppCfg.cpp b/source/App/DecoderApp/DecAppCfg.cpp index c058d6fe8c2493163d0dc40596b7654de627442b..c32fe2c41156a2968bfbc912fefe724c3e594320 100644 --- a/source/App/DecoderApp/DecAppCfg.cpp +++ b/source/App/DecoderApp/DecAppCfg.cpp @@ -87,6 +87,9 @@ bool DecAppCfg::parseCfg( int argc, char* argv[] ) ("OutputBitDepthC,d", m_outputBitDepth[CHANNEL_TYPE_CHROMA], 0, "bit depth of YUV output chroma component (default: use luma output bit-depth)") ("OutputColourSpaceConvert", outputColourSpaceConvert, string(""), "Colour space conversion to apply to input 444 video. Permitted values are (empty string=UNCHANGED) " + getListOfColourSpaceConverts(false)) ("MaxTemporalLayer,t", m_iMaxTemporalLayer, -1, "Maximum Temporal Layer to be decoded. -1 to decode all layers") +#if JVET_N0278_HLS + ("TargetLayer,p", m_iTargetLayer, -1, "Target bitstream Layer to be decoded.") +#endif ("SEIDecodedPictureHash,-dph",m_decodedPictureHashSEIEnabled, 1, "Control handling of decoded picture hash SEI messages\n" "\t1: check hash in SEI messages if available in the bitstream\n" "\t0: ignore SEI message") @@ -222,6 +225,9 @@ DecAppCfg::DecAppCfg() , m_iSkipFrame(0) // m_outputBitDepth array initialised below , m_outputColourSpaceConvert(IPCOLOURSPACE_UNCHANGED) +#if JVET_N0278_HLS +, m_iTargetLayer(0) +#endif , m_iMaxTemporalLayer(-1) , m_decodedPictureHashSEIEnabled(0) , m_decodedNoDisplaySEIEnabled(false) diff --git a/source/App/DecoderApp/DecAppCfg.h b/source/App/DecoderApp/DecAppCfg.h index a27a069284084ccdf974feb3f20c2f835f1291dd..7a24479d6a8327317e8cc4b297e59d7895ef4d86 100644 --- a/source/App/DecoderApp/DecAppCfg.h +++ b/source/App/DecoderApp/DecAppCfg.h @@ -61,6 +61,9 @@ protected: int m_iSkipFrame; ///< counter for frames prior to the random access point to skip int m_outputBitDepth[MAX_NUM_CHANNEL_TYPE]; ///< bit depth used for writing output InputColourSpaceConversion m_outputColourSpaceConvert; +#if JVET_N0278_HLS + int m_iTargetLayer; ///< target stream layer to be decoded +#endif int m_iMaxTemporalLayer; ///< maximum temporal layer to be decoded int m_decodedPictureHashSEIEnabled; ///< Checksum(3)/CRC(2)/MD5(1)/disable(0) acting on decoded picture hash SEI message diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp index de13dc4e1d373be25c5cf26d9525c99304b45fa8..6f6f549971e79bb9c672dbac6967897c185d1de8 100644 --- a/source/App/EncoderApp/EncApp.cpp +++ b/source/App/EncoderApp/EncApp.cpp @@ -85,6 +85,16 @@ void EncApp::xInitLibCfg() vps.setMaxDecPicBuffering ( m_maxDecPicBuffering[i], i ); } m_cEncLib.setVPS(&vps); +#elif JVET_N0278_HLS + VPS vps; + + vps.setMaxLayers ( 1 ); + for(int i = 0; i < MAX_TLAYER; i++) + { + vps.setVPSIncludedLayerId ( 0, i ); + } + vps.setVPSExtensionFlag ( false ); + m_cEncLib.setVPS(&vps); #endif m_cEncLib.setProfile ( m_profile); m_cEncLib.setLevel ( m_levelTier, m_level); @@ -931,7 +941,7 @@ void EncApp::rateStatsAccum(const AccessUnit& au, const std::vector<uint32_t>& a #if JVET_N0349_DPS case NAL_UNIT_DPS: #endif -#if HEVC_VPS +#if HEVC_VPS || JVET_N0278_HLS case NAL_UNIT_VPS: #endif case NAL_UNIT_SPS: diff --git a/source/App/Parcat/parcat.cpp b/source/App/Parcat/parcat.cpp index bb6f019dfd4615cecadd083aa437fd22dc7823c0..acb4df67c38ec94aee13d00f8d7475fa5f25fdd5 100644 --- a/source/App/Parcat/parcat.cpp +++ b/source/App/Parcat/parcat.cpp @@ -230,6 +230,8 @@ const char * NALU_TYPE[] = "unk", #if HEVC_VPS "VPS_NUT", +#elif JVET_N0278_HLS + "VPS_NUT", #else "unk", #endif @@ -264,7 +266,7 @@ const char * NALU_TYPE[] = "NAL_UNIT_RESERVED_VCL14", -#if HEVC_VPS +#if HEVC_VPS || JVET_N0278_HLS "NAL_UNIT_VPS", #else "NAL_UNIT_RESERVED_VCL15", @@ -466,10 +468,18 @@ std::vector<uint8_t> filter_segment(const std::vector<uint8_t> & v, int idx, int #endif #else #if JVET_N0349_DPS +#if JVET_N0278_HLS + if((idx > 1 && (nalu_type == NAL_UNIT_CODED_SLICE_IDR_W_RADL || nalu_type == NAL_UNIT_CODED_SLICE_IDR_N_LP)) || ((idx > 1 && !idr_found) && (nalu_type == NAL_UNIT_DPS || nalu_type == NAL_UNIT_VPS ||nalu_type == NAL_UNIT_SPS || nalu_type == NAL_UNIT_PPS || nalu_type == NAL_UNIT_APS)) +#else if((idx > 1 && (nalu_type == NAL_UNIT_CODED_SLICE_IDR_W_RADL || nalu_type == NAL_UNIT_CODED_SLICE_IDR_N_LP)) || ((idx > 1 && !idr_found) && (nalu_type == NAL_UNIT_DPS ||nalu_type == NAL_UNIT_SPS || nalu_type == NAL_UNIT_PPS || nalu_type == NAL_UNIT_APS)) +#endif +#else +#if JVET_N0278_HLS + if((idx > 1 && (nalu_type == NAL_UNIT_CODED_SLICE_IDR_W_RADL || nalu_type == NAL_UNIT_CODED_SLICE_IDR_N_LP)) || ((idx > 1 && !idr_found) && (nalu_type == NAL_UNIT_SPS || nalu_type == NAL_UNIT_VPS || nalu_type == NAL_UNIT_PPS || nalu_type == NAL_UNIT_APS)) #else if((idx > 1 && (nalu_type == NAL_UNIT_CODED_SLICE_IDR_W_RADL || nalu_type == NAL_UNIT_CODED_SLICE_IDR_N_LP)) || ((idx > 1 && !idr_found) && (nalu_type == NAL_UNIT_SPS || nalu_type == NAL_UNIT_PPS || nalu_type == NAL_UNIT_APS)) #endif +#endif #endif || (nalu_type == NAL_UNIT_SUFFIX_SEI && skip_next_sei)) { diff --git a/source/App/SEIRemovalApp/SEIRemovalApp.cpp b/source/App/SEIRemovalApp/SEIRemovalApp.cpp index e051551f78100dbadf9c3b104bcfaf727594da1d..a5bf76915a5356b22ffd95068d90c148cfb4f7b9 100644 --- a/source/App/SEIRemovalApp/SEIRemovalApp.cpp +++ b/source/App/SEIRemovalApp/SEIRemovalApp.cpp @@ -81,16 +81,27 @@ void read2(InputNALUnit& nalu) uint32_t nalUnitTypeLsb = bs.read(4); // nal_unit_type_lsb nalu.m_nalUnitType = (NalUnitType) ((zeroTidRequiredFlag << 4) + nalUnitTypeLsb); nalu.m_nuhLayerId = bs.read(7); // nuh_layer_id +#if EMULATION_PREVENTION_FIX + CHECK(nalu.m_nuhLayerId == 0, "nuh_layer_id_plus1 must be greater than zero"); + nalu.m_nuhLayerId--; + CHECK(nalu.m_nuhLayerId > 125, "Layer ID out of range"); +#endif CHECK((nalu.m_nuhLayerId < 0) || (nalu.m_nuhLayerId > 126), "Layer ID out of range"); uint32_t nuh_reserved_zero_bit = bs.read(1); // nuh_reserved_zero_bit CHECK(nuh_reserved_zero_bit != 0, "Reserved zero bit is not '0'"); #else bool forbidden_zero_bit = bs.read(1); // forbidden_zero_bit if(forbidden_zero_bit != 0) { THROW( "Forbidden zero-bit not '0'" );} +#if JVET_N0278_HLS + nalu.m_nalUnitType = (NalUnitType) bs.read(6); // nal_unit_type + nalu.m_temporalId = bs.read(3) - 1; // nuh_temporal_id_plus1 + nalu.m_nuhLayerId = bs.read(6); // nuh_layer_id +#else nalu.m_nalUnitType = (NalUnitType) bs.read(6); // nal_unit_type nalu.m_nuhLayerId = bs.read(6); // nuh_layer_id nalu.m_temporalId = bs.read(3) - 1; // nuh_temporal_id_plus1 #endif +#endif } uint32_t SEIRemovalApp::decode() diff --git a/source/App/StreamMergeApp/CMakeLists.txt b/source/App/StreamMergeApp/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..77c53ece6b5e192580b86a7f3b7dac9e4c0b6290 --- /dev/null +++ b/source/App/StreamMergeApp/CMakeLists.txt @@ -0,0 +1,84 @@ +# executable +set( EXE_NAME StreamMergeApp ) + +# get source files +file( GLOB SRC_FILES "*.cpp" ) + +# get include files +file( GLOB INC_FILES "*.h" ) + +# get additional libs for gcc on Ubuntu systems +if( CMAKE_SYSTEM_NAME STREQUAL "Linux" ) + if( CMAKE_CXX_COMPILER_ID STREQUAL "GNU" ) + if( USE_ADDRESS_SANITIZER ) + set( ADDITIONAL_LIBS asan ) + endif() + endif() +endif() + +# NATVIS files for Visual Studio +if( MSVC ) + file( GLOB NATVIS_FILES "../../VisualStudio/*.natvis" ) +endif() + +# add executable +add_executable( ${EXE_NAME} ${SRC_FILES} ${INC_FILES} ${NATVIS_FILES} ) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +if( SET_ENABLE_TRACING ) + if( ENABLE_TRACING ) + target_compile_definitions( ${EXE_NAME} PUBLIC ENABLE_TRACING=1 ) + else() + target_compile_definitions( ${EXE_NAME} PUBLIC ENABLE_TRACING=0 ) + endif() +endif() + +if( OpenMP_FOUND ) + if( SET_ENABLE_SPLIT_PARALLELISM ) + if( ENABLE_SPLIT_PARALLELISM ) + target_compile_definitions( ${EXE_NAME} PUBLIC ENABLE_SPLIT_PARALLELISM=1 ) + else() + target_compile_definitions( ${EXE_NAME} PUBLIC ENABLE_SPLIT_PARALLELISM=0 ) + endif() + endif() + if( SET_ENABLE_WPP_PARALLELISM ) + if( ENABLE_WPP_PARALLELISM ) + target_compile_definitions( ${EXE_NAME} PUBLIC ENABLE_WPP_PARALLELISM=1 ) + else() + target_compile_definitions( ${EXE_NAME} PUBLIC ENABLE_WPP_PARALLELISM=0 ) + endif() + endif() +else() + target_compile_definitions( ${EXE_NAME} PUBLIC ENABLE_SPLIT_PARALLELISM=0 ) + target_compile_definitions( ${EXE_NAME} PUBLIC ENABLE_WPP_PARALLELISM=0 ) +endif() + +if( CMAKE_COMPILER_IS_GNUCC AND BUILD_STATIC ) + set( ADDITIONAL_LIBS ${ADDITIONAL_LIBS} -static -static-libgcc -static-libstdc++ ) + target_compile_definitions( ${EXE_NAME} PUBLIC ENABLE_WPP_STATIC_LINK=1 ) +endif() + +target_link_libraries( ${EXE_NAME} CommonLib EncoderLib DecoderLib Utilities Threads::Threads ${ADDITIONAL_LIBS} ) + +# lldb custom data formatters +if( XCODE ) + add_dependencies( ${EXE_NAME} Install${PROJECT_NAME}LldbFiles ) +endif() + +if( CMAKE_SYSTEM_NAME STREQUAL "Linux" ) + add_custom_command( TARGET ${EXE_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy + $<$<CONFIG:Debug>:${CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG}/StreamMergeApp> + $<$<CONFIG:Release>:${CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE}/StreamMergeApp> + $<$<CONFIG:RelWithDebInfo>:${CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO}/StreamMergeApp> + $<$<CONFIG:MinSizeRel>:${CMAKE_RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL}/StreamMergeApp> + $<$<CONFIG:Debug>:${CMAKE_SOURCE_DIR}/bin/StreamMergeAppStaticd> + $<$<CONFIG:Release>:${CMAKE_SOURCE_DIR}/bin/StreamMergeAppStatic> + $<$<CONFIG:RelWithDebInfo>:${CMAKE_SOURCE_DIR}/bin/StreamMergeAppStaticp> + $<$<CONFIG:MinSizeRel>:${CMAKE_SOURCE_DIR}/bin/StreamMergeAppStaticm> ) +endif() + +# example: place header files in different folders +source_group( "Natvis Files" FILES ${NATVIS_FILES} ) + +# set the folder where to place the projects +set_target_properties( ${EXE_NAME} PROPERTIES FOLDER app LINKER_LANGUAGE CXX ) diff --git a/source/App/StreamMergeApp/StreamMergeApp.cpp b/source/App/StreamMergeApp/StreamMergeApp.cpp new file mode 100644 index 0000000000000000000000000000000000000000..557fa56b9c83dad01088c6598fe44ff2d0c9903e --- /dev/null +++ b/source/App/StreamMergeApp/StreamMergeApp.cpp @@ -0,0 +1,382 @@ +/* The copyright in this software is being made available under the BSD + * License, included below. This software may be subject to other third party + * and contributor rights, including patent rights, and no such rights are + * granted under this license. + * + * Copyright (c) 2010-2019, ITU/ISO/IEC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ITU/ISO/IEC nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + + /** \file StreamMergeApp.cpp + \brief Decoder application class + */ + +#include <list> +#include <vector> +#include <stdio.h> +#include <fcntl.h> + +#include "StreamMergeApp.h" +#include "DecoderLib/AnnexBread.h" +#include "DecoderLib/NALread.h" + + //! \ingroup DecoderApp + //! \{ + + // ==================================================================================================================== + // Constructor / destructor / initialization / destroy + // ==================================================================================================================== + +StreamMergeApp::StreamMergeApp() +{ + +} + +// ==================================================================================================================== +// Public member functions +// ==================================================================================================================== + +/** + - create internal class + - initialize internal class + - until the end of the bitstream, call decoding function in StreamMergeApp class + - delete allocated buffers + - destroy internal class + - returns the number of mismatching pictures + */ + +void read2(InputNALUnit& nalu) +{ + InputBitstream& bs = nalu.getBitstream(); + +#if JVET_N0067_NAL_Unit_Header + bool zeroTidRequiredFlag = bs.read(1); // zero_tid_required_flag + nalu.m_temporalId = bs.read(3) - 1; // nuh_temporal_id_plus1 + CHECK(nalu.m_temporalId < 0, "Temporal ID is negative."); + //When zero_tid_required_flag is equal to 1, the value of nuh_temporal_id_plus1 shall be equal to 1. + CHECK((zeroTidRequiredFlag == 1) && (nalu.m_temporalId != 0), "Temporal ID is not '0' when zero tid is required."); + uint32_t nalUnitTypeLsb = bs.read(4); // nal_unit_type_lsb + nalu.m_nalUnitType = (NalUnitType)((zeroTidRequiredFlag << 4) + nalUnitTypeLsb); + nalu.m_nuhLayerId = bs.read(7); // nuh_layer_id +#if EMULATION_PREVENTION_FIX + CHECK(nalu.m_nuhLayerId == 0, "nuh_layer_id_plus1 must be greater than zero"); + nalu.m_nuhLayerId--; + CHECK(nalu.m_nuhLayerId > 125, "Layer ID out of range"); +#endif + CHECK((nalu.m_nuhLayerId < 0) || (nalu.m_nuhLayerId > 126), "Layer ID out of range"); + uint32_t nuh_reserved_zero_bit = bs.read(1); // nuh_reserved_zero_bit + CHECK(nuh_reserved_zero_bit != 0, "Reserved zero bit is not '0'"); +#else + bool forbidden_zero_bit = bs.read(1); // forbidden_zero_bit + if (forbidden_zero_bit != 0) { THROW("Forbidden zero-bit not '0'"); } + nalu.m_nalUnitType = (NalUnitType)bs.read(6); // nal_unit_type + nalu.m_temporalId = bs.read(3) - 1; // nuh_temporal_id_plus1 + nalu.m_nuhLayerId = bs.read(6); // nuh_layer_id +#endif +} + +static void +_byteStreamNALUnit( + SingleLayerStream& bs, + std::istream& istream, + vector<uint8_t>& nalUnit, + AnnexBStats& stats) +{ + /* At the beginning of the decoding process, the decoder initialises its + * current position in the byte stream to the beginning of the byte stream. + * It then extracts and discards each leading_zero_8bits syntax element (if + * present), moving the current position in the byte stream forward one + * byte at a time, until the current position in the byte stream is such + * that the next four bytes in the bitstream form the four-byte sequence + * 0x00000001. + */ +#if RExt__DECODER_DEBUG_BIT_STATISTICS + CodingStatistics::SStat &statBits = CodingStatistics::GetStatisticEP(STATS__NAL_UNIT_PACKING); +#endif + while ((bs.eofBeforeNBytes(24 / 8, istream) || bs.peekBytes(24 / 8, istream) != 0x000001) + && (bs.eofBeforeNBytes(32 / 8, istream) || bs.peekBytes(32 / 8, istream) != 0x00000001)) + { + uint8_t leading_zero_8bits = bs.readByte(istream); +#if RExt__DECODER_DEBUG_BIT_STATISTICS + statBits.bits += 8; statBits.count++; +#endif + if (leading_zero_8bits != 0) { THROW("Leading zero bits not zero"); } + stats.m_numLeadingZero8BitsBytes++; + } + + /* 1. When the next four bytes in the bitstream form the four-byte sequence + * 0x00000001, the next byte in the byte stream (which is a zero_byte + * syntax element) is extracted and discarded and the current position in + * the byte stream is set equal to the position of the byte following this + * discarded byte. + */ + /* NB, the previous step guarantees this will succeed -- if EOF was + * encountered, an exception will stop execution getting this far */ + if (bs.peekBytes(24 / 8, istream) != 0x000001) + { + uint8_t zero_byte = bs.readByte(istream); +#if RExt__DECODER_DEBUG_BIT_STATISTICS + statBits.bits += 8; statBits.count++; +#endif + CHECK(zero_byte != 0, "Zero byte not '0'"); + stats.m_numZeroByteBytes++; + } + + /* 2. The next three-byte sequence in the byte stream (which is a + * start_code_prefix_one_3bytes) is extracted and discarded and the current + * position in the byte stream is set equal to the position of the byte + * following this three-byte sequence. + */ + /* NB, (1) guarantees that the next three bytes are 0x00 00 01 */ + uint32_t start_code_prefix_one_3bytes = bs.readBytes(24 / 8, istream); +#if RExt__DECODER_DEBUG_BIT_STATISTICS + statBits.bits += 24; statBits.count += 3; +#endif + if (start_code_prefix_one_3bytes != 0x000001) { THROW("Invalid code prefix"); } + stats.m_numStartCodePrefixBytes += 3; + + /* 3. NumBytesInNALunit is set equal to the number of bytes starting with + * the byte at the current position in the byte stream up to and including + * the last byte that precedes the location of any of the following + * conditions: + * a. A subsequent byte-aligned three-byte sequence equal to 0x000000, or + * b. A subsequent byte-aligned three-byte sequence equal to 0x000001, or + * c. The end of the byte stream, as determined by unspecified means. + */ + /* 4. NumBytesInNALunit bytes are removed from the bitstream and the + * current position in the byte stream is advanced by NumBytesInNALunit + * bytes. This sequence of bytes is nal_unit( NumBytesInNALunit ) and is + * decoded using the NAL unit decoding process + */ + /* NB, (unsigned)x > 2 implies n!=0 && n!=1 */ +#if RExt__DECODER_DEBUG_BIT_STATISTICS + CodingStatistics::SStat &bodyStats = CodingStatistics::GetStatisticEP(STATS__NAL_UNIT_TOTAL_BODY); +#endif + while (bs.eofBeforeNBytes(24 / 8, istream) || bs.peekBytes(24 / 8, istream) > 2) + { +#if RExt__DECODER_DEBUG_BIT_STATISTICS + uint8_t thebyte = bs.readByte(); bodyStats.bits += 8; bodyStats.count++; + nalUnit.push_back(thebyte); +#else + nalUnit.push_back(bs.readByte(istream)); +#endif + } + + /* 5. When the current position in the byte stream is: + * - not at the end of the byte stream (as determined by unspecified means) + * - and the next bytes in the byte stream do not start with a three-byte + * sequence equal to 0x000001 + * - and the next bytes in the byte stream do not start with a four byte + * sequence equal to 0x00000001, + * the decoder extracts and discards each trailing_zero_8bits syntax + * element, moving the current position in the byte stream forward one byte + * at a time, until the current position in the byte stream is such that: + * - the next bytes in the byte stream form the four-byte sequence + * 0x00000001 or + * - the end of the byte stream has been encountered (as determined by + * unspecified means). + */ + /* NB, (3) guarantees there are at least three bytes available or none */ + while ((bs.eofBeforeNBytes(24 / 8, istream) || bs.peekBytes(24 / 8, istream) != 0x000001) + && (bs.eofBeforeNBytes(32 / 8, istream) || bs.peekBytes(32 / 8, istream) != 0x00000001)) + { + uint8_t trailing_zero_8bits = bs.readByte(istream); +#if RExt__DECODER_DEBUG_BIT_STATISTICS + statBits.bits += 8; statBits.count++; +#endif + CHECK(trailing_zero_8bits != 0, "Trailing zero bits not '0'"); + stats.m_numTrailingZero8BitsBytes++; + } +} + +/** + * Parse an AVC AnnexB Bytestream bs to extract a single nalUnit + * while accumulating bytestream statistics into stats. + * + * Returns false if EOF was reached (NB, nalunit data may be valid), + * otherwise true. + */ +bool +byteStreamNALUnit( + SingleLayerStream& bs, + std::istream& istream, + vector<uint8_t>& nalUnit, + AnnexBStats& stats) +{ + bool eof = false; + try + { + _byteStreamNALUnit(bs, istream, nalUnit, stats); + } + catch (...) + { + eof = true; + } + stats.m_numBytesInNALUnit = uint32_t(nalUnit.size()); + return eof; +} + +void StreamMergeApp::writeNewVPS(ostream& out, int nLayerId, int nTemporalId) +{ + //write NALU header + OutputBitstream bsNALUHeader; + static const uint8_t start_code_prefix[] = { 0,0,0,1 }; + +#if JVET_N0067_NAL_Unit_Header + bsNALUHeader.write(1, 1); // zero_tid_required_flag + bsNALUHeader.write(nTemporalId + 1, 3); // nuh_temporal_id_plus1 + uint32_t nalUnitTypeLsb = NAL_UNIT_VPS - (1 << 4); + bsNALUHeader.write(nalUnitTypeLsb, 4); // nal_unit_type_lsb +#if EMULATION_PREVENTION_FIX + bsNALUHeader.write(nLayerId + 1, 7); // nuh_layer_id +#else + bsNALUHeader.write(nLayerId, 7); // nuh_layer_id +#endif + bsNALUHeader.write(0, 1); // nuh_reserved_zero_bit +#else + bsNALUHeader.write(0, 1); // forbidden_zero_bit + bsNALUHeader.write(NAL_UNIT_VPS, 6); // nal_unit_type + bsNALUHeader.write(1, 3); // nuh_temporal_id_plus1 + bsNALUHeader.write(nLayerId, 6); // nuh_layer_id +#endif + + out.write(reinterpret_cast<const char*>(start_code_prefix), 4); + out.write(reinterpret_cast<const char*>(bsNALUHeader.getByteStream()), bsNALUHeader.getByteStreamLength()); + + //write VPS + OutputBitstream bsVPS; + HLSWriter m_HLSWriter; + + m_HLSWriter.setBitstream(&bsVPS); + m_HLSWriter.codeVPS(&vps); + + out.write(reinterpret_cast<const char*>(bsVPS.getByteStream()), bsVPS.getByteStreamLength()); + + return; +} + +uint32_t StreamMergeApp::mergeStreams() +{ + ifstream bitstreamFileIn[MAX_VPS_LAYERS]; + ofstream bitstreamFileOut(m_bitstreamFileNameOut.c_str(), ifstream::out | ifstream::binary); + int nNumValidStr = m_numInputStreams; + + for (int i = 0; i < m_numInputStreams; i++) + { + bitstreamFileIn[i].open(m_bitstreamFileNameIn[i].c_str(), ifstream::in | ifstream::binary); + + if (!bitstreamFileIn[i]) + { + EXIT("failed to open bitstream file " << m_bitstreamFileNameIn[i].c_str() << " for reading"); + } + + bitstreamFileIn[i].clear(); + bitstreamFileIn[i].seekg(0, ios::beg); + } + + SingleLayerStream bytestream[MAX_VPS_LAYERS]; + + for (int i = 0; i < m_numInputStreams; i++) + bytestream[i].init(bitstreamFileIn[i]); + + //set VPS which will be replicated for all layers but with differnt nul_layer_id + vps.setMaxLayers(m_numInputStreams); + for (int i = 0; i < m_numInputStreams; i++) + vps.setVPSIncludedLayerId(i < 63 ? i : i + 1, i); //value 63 is reserved + + vps.setVPSExtensionFlag(false); + + //Loop all input bitstreams to interleave their NALUs + while (nNumValidStr) + { + //loop over all input streams + for (int i = 0; i < m_numInputStreams; i++) + { + uint8_t layerId = i < 63 ? i : i + 1; + + if (!bitstreamFileIn[i]) + continue; + + AnnexBStats stats = AnnexBStats(); + + InputNALUnit nalu; + + byteStreamNALUnit(bytestream[i], bitstreamFileIn[i], 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 + */ + std::cerr << "Warning: Attempt to decode an empty NAL unit" << std::endl; + } + else + { + read2(nalu); + + if (nalu.m_nalUnitType == NAL_UNIT_VPS) + { + writeNewVPS(bitstreamFileOut, layerId, nalu.m_temporalId); + printf("Write new VPS for stream %d\n", i); + + continue; + } + + int iNumZeros = stats.m_numLeadingZero8BitsBytes + stats.m_numZeroByteBytes + stats.m_numStartCodePrefixBytes - 1; + char ch = 0; + for (int i = 0; i < iNumZeros; i++) { bitstreamFileOut.write(&ch, 1); } + ch = 1; bitstreamFileOut.write(&ch, 1); + + //update the nul_layer_id + uint8_t *p = (uint8_t*)nalu.getBitstream().getFifo().data(); +#if JVET_N0067_NAL_Unit_Header +#if EMULATION_PREVENTION_FIX + p[1] = ((layerId + 1) << 1) & 0xff; +#else + p[1] = (layerId << 1) & 0xff; +#endif +#else + p[1] = (p[1] & 0xc0) | layerId; +#endif + + bitstreamFileOut.write((const char*)p, nalu.getBitstream().getFifo().size()); + + printf("Merge NALU type %d from stream %d\n", nalu.m_nalUnitType, i); + } + + if (!bitstreamFileIn[i]) + nNumValidStr--; + } + } + + return 0; +} + +//! \} diff --git a/source/App/StreamMergeApp/StreamMergeApp.h b/source/App/StreamMergeApp/StreamMergeApp.h new file mode 100644 index 0000000000000000000000000000000000000000..cca09b6d2b6edae2bcb725174476e69b409cfdfa --- /dev/null +++ b/source/App/StreamMergeApp/StreamMergeApp.h @@ -0,0 +1,196 @@ +/* The copyright in this software is being made available under the BSD + * License, included below. This software may be subject to other third party + * and contributor rights, including patent rights, and no such rights are + * granted under this license. + * + * Copyright (c) 2010-2019, ITU/ISO/IEC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ITU/ISO/IEC nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** \file SEIRemovalApp.h + \brief Decoder application class (header) +*/ + +#ifndef __STREAMMERGEAPP__ +#define __STREAMMERGEAPP__ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include <stdio.h> +#include <fstream> +#include <iostream> +#include "CommonLib/CommonDef.h" +#include "VLCWriter.h" +#include "CABACWriter.h" +#include "AnnexBread.h" +#include "StreamMergeAppCfg.h" + +using namespace std; + +// ==================================================================================================================== +// Class definition +// ==================================================================================================================== + +/// decoder application class +class StreamMergeApp : public StreamMergeAppCfg +{ + +public: + StreamMergeApp(); + virtual ~StreamMergeApp () {} + + VPS vps; + + uint32_t mergeStreams (); ///< main stream merging function + void writeNewVPS (ostream& out, int nNumLayers, int nTemporalId); +}; + + +class SingleLayerStream +{ +public: + /** + * Create a bytestream reader that will extract bytes from + * istream. + * + * NB, it isn't safe to access istream while in use by a + * InputByteStream. + * + * Side-effects: the exception mask of istream is set to eofbit + */ + SingleLayerStream() + : m_NumFutureBytes(0) + , m_FutureBytes(0) + { + } + + /** + * Reset the internal state. Must be called if input stream is + * modified externally to this class + */ + void reset() + { + m_NumFutureBytes = 0; + m_FutureBytes = 0; + } + + void init(std::istream& istream) + { + istream.exceptions(std::istream::eofbit | std::istream::badbit); + } + + /** + * returns true if an EOF will be encountered within the next + * n bytes. + */ + bool eofBeforeNBytes(uint32_t n, std::istream& m_Input) + { + CHECK(n > 4, "Unsupported look-ahead value"); + if (m_NumFutureBytes >= n) + { + return false; + } + + n -= m_NumFutureBytes; + try + { + for (uint32_t i = 0; i < n; i++) + { + m_FutureBytes = (m_FutureBytes << 8) | m_Input.get(); + m_NumFutureBytes++; + } + } + catch (...) + { + return true; + } + return false; + } + + /** + * return the next n bytes in the stream without advancing + * the stream pointer. + * + * Returns: an unsigned integer representing an n byte bigendian + * word. + * + * If an attempt is made to read past EOF, an n-byte word is + * returned, but the portion that required input bytes beyond EOF + * is undefined. + * + */ + uint32_t peekBytes(uint32_t n, std::istream& m_Input) + { + eofBeforeNBytes(n, m_Input); + return m_FutureBytes >> 8 * (m_NumFutureBytes - n); + } + + /** + * consume and return one byte from the input. + * + * If bytestream is already at EOF prior to a call to readByte(), + * an exception std::ios_base::failure is thrown. + */ + uint8_t readByte(std::istream& m_Input) + { + if (!m_NumFutureBytes) + { + uint8_t byte = m_Input.get(); + return byte; + } + m_NumFutureBytes--; + uint8_t wanted_byte = m_FutureBytes >> 8 * m_NumFutureBytes; + m_FutureBytes &= ~(0xff << 8 * m_NumFutureBytes); + return wanted_byte; + } + + /** + * consume and return n bytes from the input. n bytes from + * bytestream are interpreted as bigendian when assembling + * the return value. + */ + uint32_t readBytes(uint32_t n, std::istream& m_Input) + { + uint32_t val = 0; + for (uint32_t i = 0; i < n; i++) + { + val = (val << 8) | readByte(m_Input); + } + return val; + } + +private: + uint32_t m_NumFutureBytes; /* number of valid bytes in m_FutureBytes */ + uint32_t m_FutureBytes; /* bytes that have been peeked */ +}; + +bool byteStreamNALUnit(SingleLayerStream& bs, std::istream& istream, vector<uint8_t>& nalUnit, AnnexBStats& stats); + +#endif // __STREAMMERGEAPP__ + diff --git a/source/App/StreamMergeApp/StreamMergeAppCfg.cpp b/source/App/StreamMergeApp/StreamMergeAppCfg.cpp new file mode 100644 index 0000000000000000000000000000000000000000..608e3aa59df130bf8c344cc89682857e1c74217f --- /dev/null +++ b/source/App/StreamMergeApp/StreamMergeAppCfg.cpp @@ -0,0 +1,142 @@ +/* The copyright in this software is being made available under the BSD + * License, included below. This software may be subject to other third party + * and contributor rights, including patent rights, and no such rights are + * granted under this license. + * + * Copyright (c) 2010-2019, ITU/ISO/IEC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ITU/ISO/IEC nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + + /** \file StreamMergeAppCfg.cpp + \brief Decoder configuration class + */ + +#include <cstdio> +#include <cstring> +#include <string> +#include "StreamMergeAppCfg.h" +#include "Utilities/program_options_lite.h" + +using namespace std; +namespace po = df::program_options_lite; + +//! \ingroup DecoderApp +//! \{ + +// ==================================================================================================================== +// Public member functions +// ==================================================================================================================== + +/** \param argc number of arguments + \param argv array of arguments + */ +bool StreamMergeAppCfg::parseCfg(int argc, char* argv[]) +{ +#if 1 + int i; + + m_numInputStreams = argc - 2; + + for (i = 0; i < m_numInputStreams; i++) + { + m_bitstreamFileNameIn[i] = argv[i + 1]; + } + + m_bitstreamFileNameOut = argv[i + 1]; +#else + bool do_help = false; + int warnUnknowParameter = 0; + po::Options opts; + opts.addOptions() + + ("help", do_help, false, "this help text") + ("BitstreamFileIn0,-a", m_bitstreamFileNameIn[0], string(""), "bitstream input file name") + ("BitstreamFileIn1,-b", m_bitstreamFileNameIn[1], string(""), "bitstream input file name") + ("BitstreamFileOut,o", m_bitstreamFileNameOut, string(""), "bitstream output file name") + ; + + po::setDefaults(opts); + po::ErrorReporter err; + const list<const char*>& argv_unhandled = po::scanArgv(opts, argc, (const char**)argv, err); + + for (list<const char*>::const_iterator it = argv_unhandled.begin(); it != argv_unhandled.end(); it++) + { + std::cerr << "Unhandled argument ignored: " << *it << std::endl; + } + + if (argc == 1 || do_help) + { + po::doHelp(cout, opts); + return false; + } + + if (err.is_errored) + { + if (!warnUnknowParameter) + { + /* errors have already been reported to stderr */ + return false; + } + } + + m_numInputStreams = 0; + for (int i = 0; i < MAX_VPS_LAYERS; i++) + { + if (!m_bitstreamFileNameIn[i].empty()) + m_numInputStreams++; + } + + if (m_numInputStreams < 2) + { + std::cerr << "Need at least two input bitstreams, aborting" << std::endl; + return false; + } + + if (m_bitstreamFileNameOut.empty()) + { + std::cerr << "No output file specified, aborting" << std::endl; + return false; + } +#endif + + return true; +} + +StreamMergeAppCfg::StreamMergeAppCfg() + : m_bitstreamFileNameOut() + , m_numInputStreams(0) +{ + for (int i = 0; i < MAX_VPS_LAYERS; i++) + m_bitstreamFileNameIn[i] = ""; +} + +StreamMergeAppCfg::~StreamMergeAppCfg() +{ + +} + +//! \} diff --git a/source/App/StreamMergeApp/StreamMergeAppCfg.h b/source/App/StreamMergeApp/StreamMergeAppCfg.h new file mode 100644 index 0000000000000000000000000000000000000000..d03a20ab5a2ed698c0ea2d2a19ddaa6643c132a2 --- /dev/null +++ b/source/App/StreamMergeApp/StreamMergeAppCfg.h @@ -0,0 +1,74 @@ +/* The copyright in this software is being made available under the BSD + * License, included below. This software may be subject to other third party + * and contributor rights, including patent rights, and no such rights are + * granted under this license. + * + * Copyright (c) 2010-2019, ITU/ISO/IEC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ITU/ISO/IEC nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** \file StreamMergeAppCfg.h + \brief Stream merge app configuration class (header) +*/ + +#ifndef __STREAMMERGEAPPCFG__ +#define __STREAMMERGEAPPCFG__ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "CommonLib/CommonDef.h" +#include <vector> + +//! \ingroup DecoderApp +//! \{ + +// ==================================================================================================================== +// Class definition +// ==================================================================================================================== + +/// Decoder configuration class +class StreamMergeAppCfg +{ +protected: + std::string m_bitstreamFileNameIn[MAX_VPS_LAYERS]; ///< output bitstream file name + std::string m_bitstreamFileNameOut; ///< input bitstream file name + int m_numInputStreams; ///< number of input bitstreams + +public: + StreamMergeAppCfg(); + virtual ~StreamMergeAppCfg(); + + bool parseCfg ( int argc, char* argv[] ); ///< initialize option class from configuration +}; + +//! \} + +#endif // __STREAMMERGEAPPCFG__ + + diff --git a/source/App/StreamMergeApp/StreamMergeMain.cpp b/source/App/StreamMergeApp/StreamMergeMain.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9c693299ed71fea27fa98784a35c6a75b57ae835 --- /dev/null +++ b/source/App/StreamMergeApp/StreamMergeMain.cpp @@ -0,0 +1,94 @@ +/* The copyright in this software is being made available under the BSD + * License, included below. This software may be subject to other third party + * and contributor rights, including patent rights, and no such rights are + * granted under this license. + * + * Copyright (c) 2010-2019, ITU/ISO/IEC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ITU/ISO/IEC nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + + /** \file StreamMergeMain.cpp + \brief Stream merge application main + */ + +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include "StreamMergeApp.h" +#include "program_options_lite.h" + + //! \ingroup DecoderApp + //! \{ + + // ==================================================================================================================== + // Main function + // ==================================================================================================================== + +int main(int argc, char* argv[]) +{ + int returnCode = EXIT_SUCCESS; + + if (argc < 4) + { + printf("usage: %s <bitstream1> <bitstream2> [<bitstream3> ...] <outfile>\n", argv[0]); + return -1; + } + + // print information + fprintf(stdout, "\n"); + fprintf(stdout, "VVCSoftware: VTM Version %s ", VTM_VERSION); + fprintf(stdout, "\n"); + + StreamMergeApp *pStrMergeApp = new StreamMergeApp; + // parse configuration + if (!pStrMergeApp->parseCfg(argc, argv)) + { + returnCode = EXIT_FAILURE; + return returnCode; + } + + // starting time + double dResult; + clock_t lBefore = clock(); + + // call decoding function + if (0 != pStrMergeApp->mergeStreams()) + { + printf("\n\n***ERROR*** A merge error happened\n"); + returnCode = EXIT_FAILURE; + } + + // ending time + dResult = (double)(clock() - lBefore) / CLOCKS_PER_SEC; + printf("\n Total Time: %12.3f sec.\n", dResult); + + delete pStrMergeApp; + + return returnCode; +} + +//! \} diff --git a/source/Lib/CommonLib/CodingStructure.cpp b/source/Lib/CommonLib/CodingStructure.cpp index 7201322c5065cdabea20f2f2222ab70a949a128a..8734f27d6adc619a0b255f0553d044e8e87a8892 100644 --- a/source/Lib/CommonLib/CodingStructure.cpp +++ b/source/Lib/CommonLib/CodingStructure.cpp @@ -774,6 +774,8 @@ void CodingStructure::initSubStructure( CodingStructure& subStruct, const Channe subStruct.sps = sps; #if HEVC_VPS subStruct.vps = vps; +#elif JVET_N0278_HLS + subStruct.vps = vps; #endif subStruct.pps = pps; #if JVET_N0415_CTB_ALF diff --git a/source/Lib/CommonLib/CodingStructure.h b/source/Lib/CommonLib/CodingStructure.h index e6bcaa9601d2247d4abcf87c8528307510e666e8..97ca7aceaa958db30098cb522d81c00d4532fe38 100644 --- a/source/Lib/CommonLib/CodingStructure.h +++ b/source/Lib/CommonLib/CodingStructure.h @@ -101,6 +101,8 @@ public: #endif #if HEVC_VPS const VPS *vps; +#elif JVET_N0278_HLS + const VPS *vps; #endif const PreCalcValues* pcv; diff --git a/source/Lib/CommonLib/CommonDef.h b/source/Lib/CommonLib/CommonDef.h index d1c20c58e119a3becb30cfbfc1585dfe2e757a48..0db2cdcda127eecb4ab106db4b8d0e79ee79a3cd 100644 --- a/source/Lib/CommonLib/CommonDef.h +++ b/source/Lib/CommonLib/CommonDef.h @@ -173,6 +173,9 @@ static const int MAX_NESTING_NUM_LAYER = 64; static const int MAX_VPS_NUM_HRD_PARAMETERS = 1; static const int MAX_VPS_OP_SETS_PLUS1 = 1024; static const int MAX_VPS_NUH_RESERVED_ZERO_LAYER_ID_PLUS1 = 1; +#elif JVET_N0278_HLS +static const int MAX_VPS_NUM_HRD_PARAMETERS = 1; +static const int MAX_VPS_LAYERS = 256; #endif static const int MAXIMUM_INTRA_FILTERED_WIDTH = 16; diff --git a/source/Lib/CommonLib/Rom.cpp b/source/Lib/CommonLib/Rom.cpp index d3be0cc6e3f86906161560a082e776a662c33791..2c72157719a2487c64552402157fd4236ae626b3 100644 --- a/source/Lib/CommonLib/Rom.cpp +++ b/source/Lib/CommonLib/Rom.cpp @@ -92,7 +92,7 @@ const char* nalUnitTypeToString(NalUnitType type) case NAL_UNIT_CODED_SLICE_CRA: return "CRA"; case NAL_UNIT_CODED_SLICE_RADL: return "RADL"; case NAL_UNIT_CODED_SLICE_RASL: return "RASL"; -#if HEVC_VPS +#if HEVC_VPS || JVET_N0278_HLS case NAL_UNIT_VPS: return "VPS"; #endif case NAL_UNIT_SPS: return "SPS"; @@ -122,7 +122,7 @@ const char* nalUnitTypeToString(NalUnitType type) case NAL_UNIT_CODED_SLICE_RADL_N: return "RADL_N"; case NAL_UNIT_CODED_SLICE_RASL_R: return "RASL_R"; case NAL_UNIT_CODED_SLICE_RASL_N: return "RASL_N"; -#if HEVC_VPS +#if HEVC_VPS || JVET_N0278_HLS case NAL_UNIT_VPS: return "VPS"; #endif case NAL_UNIT_SPS: return "SPS"; diff --git a/source/Lib/CommonLib/Slice.cpp b/source/Lib/CommonLib/Slice.cpp index a2fc1c1110a2658cf33510d93ddfb435dbce0942..855a66c99f708732f7ec13180b3e5746318dd1ad 100644 --- a/source/Lib/CommonLib/Slice.cpp +++ b/source/Lib/CommonLib/Slice.cpp @@ -1741,6 +1741,24 @@ VPS::VPS() } } +VPS::~VPS() +{ +} +#elif JVET_N0278_HLS +// ------------------------------------------------------------------------------------------------ +// Video parameter set (VPS) +// ------------------------------------------------------------------------------------------------ +VPS::VPS() + : m_VPSId(0) + , m_uiMaxLayers(1) + , m_vpsExtensionFlag() +{ + for (int i = 0; i < MAX_VPS_LAYERS; i++) + { + m_vpsIncludedLayerId[i] = 0; + } +} + VPS::~VPS() { } diff --git a/source/Lib/CommonLib/Slice.h b/source/Lib/CommonLib/Slice.h index 5bee9e4db844b7efbad90ec88720096b28a680c2..f0251bbdde79f45135c39930d63fcae9bc31da82 100644 --- a/source/Lib/CommonLib/Slice.h +++ b/source/Lib/CommonLib/Slice.h @@ -593,6 +593,33 @@ public: TimingInfo* getTimingInfo() { return &m_timingInfo; } const TimingInfo* getTimingInfo() const { return &m_timingInfo; } }; +#elif JVET_N0278_HLS +class VPS +{ +private: + int m_VPSId; + uint32_t m_uiMaxLayers; + + uint32_t m_vpsIncludedLayerId[MAX_VPS_LAYERS]; + bool m_vpsExtensionFlag; + +public: + VPS(); + + virtual ~VPS(); + + int getVPSId() const { return m_VPSId; } + void setVPSId(int i) { m_VPSId = i; } + + uint32_t getMaxLayers() const { return m_uiMaxLayers; } + void setMaxLayers(uint32_t l) { m_uiMaxLayers = l; } + + bool getVPSExtensionFlag() const { return m_vpsExtensionFlag; } + void setVPSExtensionFlag(bool t) { m_vpsExtensionFlag = t; } + + void setVPSIncludedLayerId(uint32_t v, uint32_t Layer) { m_vpsIncludedLayerId[Layer] = v; } + uint32_t getVPSIncludedLayerId(uint32_t Layer) const { return m_vpsIncludedLayerId[Layer]; } +}; #endif class Window diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h index 6b94ceba556d0c126b873c9067519b84ed9fd92a..5148cb3cbc68f5b69e2513cbbd26876a9a79ea32 100644 --- a/source/Lib/CommonLib/TypeDef.h +++ b/source/Lib/CommonLib/TypeDef.h @@ -50,6 +50,8 @@ #include <assert.h> #include <cassert> +#define JVET_N0278_HLS 1 // JVET-N0278: HLS for MPEG requirements on immersive media delivery and access + #define JVET_N0063_VUI 1 // JVET-N0063: Video Usability Information #define JVET_N0847_SCALING_LISTS 1 //1: default mode, 2: user defined mode @@ -1096,7 +1098,7 @@ enum NalUnitType NAL_UNIT_RESERVED_VCL30, NAL_UNIT_RESERVED_VCL31, -#if HEVC_VPS +#if HEVC_VPS || JVET_N0278_HLS NAL_UNIT_VPS, // 32 #else NAL_UNIT_RESERVED_32, diff --git a/source/Lib/CommonLib/Unit.h b/source/Lib/CommonLib/Unit.h index 7712a155e6c7b5b100d7739869fb45ca8c4bd019..0a2a6fc35bf55a42766e71b3c8a0ab553506c1ab 100644 --- a/source/Lib/CommonLib/Unit.h +++ b/source/Lib/CommonLib/Unit.h @@ -267,7 +267,7 @@ struct UnitAreaRelative : public UnitArea }; class SPS; -#if HEVC_VPS +#if HEVC_VPS || JVET_N0278_HLS class VPS; #endif #if JVET_N0349_DPS diff --git a/source/Lib/DecoderLib/DecLib.cpp b/source/Lib/DecoderLib/DecLib.cpp index 9bbe2f9a4bdddb1d4ed8b1b32217e03ea5208daa..c6e0f9fd8ac20bb867ae340f0bbc357c45298db3 100644 --- a/source/Lib/DecoderLib/DecLib.cpp +++ b/source/Lib/DecoderLib/DecLib.cpp @@ -1421,13 +1421,15 @@ bool DecLib::xDecodeSlice(InputNALUnit &nalu, int &iSkipFrame, int iPOCLastDispl return false; } -#if HEVC_VPS +#if HEVC_VPS || JVET_N0278_HLS void DecLib::xDecodeVPS( InputNALUnit& nalu ) { VPS* vps = new VPS(); m_HLSReader.setBitstream( &nalu.getBitstream() ); m_HLSReader.parseVPS( vps ); +#if !JVET_N0278_HLS m_parameterSetManager.storeVPS( vps, nalu.getBitstream().getFifo() ); +#endif } #endif @@ -1477,7 +1479,11 @@ bool DecLib::decode(InputNALUnit& nalu, int& iSkipFrame, int& iPOCLastDisplay) { bool ret; // ignore all NAL units of layers > 0 +#if JVET_N0278_HLS + if (getTargetDecLayer() >= 0 && nalu.m_nuhLayerId != getTargetDecLayer()) //TBC: ignore bitstreams whose nuh_layer_id is not the target layer id +#else if (nalu.m_nuhLayerId > 0) +#endif { msg( WARNING, "Warning: found NAL unit with nuh_layer_id equal to %d. Ignoring.\n", nalu.m_nuhLayerId); return false; @@ -1485,7 +1491,7 @@ bool DecLib::decode(InputNALUnit& nalu, int& iSkipFrame, int& iPOCLastDisplay) switch (nalu.m_nalUnitType) { -#if HEVC_VPS +#if HEVC_VPS || JVET_N0278_HLS case NAL_UNIT_VPS: xDecodeVPS( nalu ); return false; @@ -1617,7 +1623,7 @@ bool DecLib::decode(InputNALUnit& nalu, int& iSkipFrame, int& iPOCLastDisplay) case NAL_UNIT_RESERVED_VCL29: case NAL_UNIT_RESERVED_VCL30: case NAL_UNIT_RESERVED_VCL31: -#if !HEVC_VPS +#if !HEVC_VPS && !JVET_N0278_HLS case NAL_UNIT_RESERVED_32: #endif #else diff --git a/source/Lib/DecoderLib/DecLib.h b/source/Lib/DecoderLib/DecLib.h index fcce378de45560d43017b3895fb97e9a9faa392d..8d5c2185bc619d50394b33ff8c22d28a2ddb462a 100644 --- a/source/Lib/DecoderLib/DecLib.h +++ b/source/Lib/DecoderLib/DecLib.h @@ -82,6 +82,10 @@ private: SEIMessages m_SEIs; ///< List of SEI messages that have been received before the first slice and between slices, excluding prefix SEIs... +#if JVET_N0278_HLS + int m_iTargetLayer; ///< target stream layer to be decoded +#endif + // functional classes IntraPrediction m_cIntraPred; InterPrediction m_cInterPred; @@ -148,6 +152,11 @@ public: void finishPictureLight(int& poc, PicList*& rpcListPic ); void checkNoOutputPriorPics (PicList* rpcListPic); +#if JVET_N0278_HLS + void setTargetDecLayer (int val) { m_iTargetLayer = val; } + int getTargetDecLayer() { return m_iTargetLayer; } +#endif + bool getNoOutputPriorPicsFlag () const { return m_isNoOutputPriorPics; } void setNoOutputPriorPicsFlag (bool val) { m_isNoOutputPriorPics = val; } void setFirstSliceInPicture (bool val) { m_bFirstSliceInPicture = val; } @@ -168,7 +177,7 @@ protected: void xActivateParameterSets(); bool xDecodeSlice(InputNALUnit &nalu, int &iSkipFrame, int iPOCLastDisplay); -#if HEVC_VPS +#if HEVC_VPS || JVET_N0278_HLS void xDecodeVPS( InputNALUnit& nalu ); #endif #if JVET_N0349_DPS diff --git a/source/Lib/DecoderLib/NALread.cpp b/source/Lib/DecoderLib/NALread.cpp index 067f9a61fd9b0ed755f1d3dd4101e50ef88694d8..403e9e0ffab937c012d446343e7f5573fe72d8e5 100644 --- a/source/Lib/DecoderLib/NALread.cpp +++ b/source/Lib/DecoderLib/NALread.cpp @@ -216,6 +216,9 @@ void readNalUnitHeader(InputNALUnit& nalu) || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_IDR_W_RADL || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_IDR_N_LP || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_CRA +#if JVET_N0278_HLS + || nalu.m_nalUnitType == NAL_UNIT_VPS +#endif || nalu.m_nalUnitType == NAL_UNIT_SPS || nalu.m_nalUnitType == NAL_UNIT_EOS || nalu.m_nalUnitType == NAL_UNIT_EOB @@ -224,6 +227,9 @@ void readNalUnitHeader(InputNALUnit& nalu) CHECK(nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_IDR_W_RADL || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_IDR_N_LP || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_CRA +#if JVET_N0278_HLS + || nalu.m_nalUnitType == NAL_UNIT_VPS +#endif || nalu.m_nalUnitType == NAL_UNIT_SPS || nalu.m_nalUnitType == NAL_UNIT_EOS || nalu.m_nalUnitType == NAL_UNIT_EOB diff --git a/source/Lib/DecoderLib/VLCReader.cpp b/source/Lib/DecoderLib/VLCReader.cpp index f43efc759b6bf19134b8bba77a8a30290cd6f2a4..25efef21692856b71bfce61548ad69d1333d425a 100644 --- a/source/Lib/DecoderLib/VLCReader.cpp +++ b/source/Lib/DecoderLib/VLCReader.cpp @@ -1745,6 +1745,33 @@ void HLSyntaxReader::parseVPS(VPS* pcVPS) xReadRbspTrailingBits(); } +#elif JVET_N0278_HLS +void HLSyntaxReader::parseVPS(VPS* pcVPS) +{ +#if ENABLE_TRACING + xTraceVPSHeader(); +#endif + uint32_t uiCode; + + READ_CODE(4, uiCode, "vps_video_parameter_set_id"); pcVPS->setVPSId(uiCode); + READ_CODE(8, uiCode, "vps_max_layers_minus1"); pcVPS->setMaxLayers(uiCode + 1); CHECK(uiCode + 1 > MAX_VPS_LAYERS, "Invalid code"); + for (uint32_t i = 0; i <= pcVPS->getMaxLayers() - 1; i++) + { + READ_CODE(7, uiCode, "vps_included_layer_id"); pcVPS->setVPSIncludedLayerId(uiCode, i); + READ_FLAG(uiCode, "vps_reserved_zero_1bit"); + } + + READ_FLAG(uiCode, "vps_extension_flag"); + if (uiCode) + { + while (xMoreRbspData()) + { + READ_FLAG(uiCode, "vps_extension_data_flag"); + } + } + + xReadRbspTrailingBits(); +} #endif void HLSyntaxReader::parseSliceHeader (Slice* pcSlice, ParameterSetManager *parameterSetManager, const int prevTid0POC) diff --git a/source/Lib/DecoderLib/VLCReader.h b/source/Lib/DecoderLib/VLCReader.h index ec57f76ec51064efeced482c824f806aed901888..f5035eb44d302b2a6cbab05e2200524d759ed3d9 100644 --- a/source/Lib/DecoderLib/VLCReader.h +++ b/source/Lib/DecoderLib/VLCReader.h @@ -147,7 +147,7 @@ protected: public: void setBitstream ( InputBitstream* p ) { m_pcBitstream = p; } -#if HEVC_VPS +#if HEVC_VPS || JVET_N0278_HLS void parseVPS ( VPS* pcVPS ); #endif #if JVET_N0349_DPS diff --git a/source/Lib/EncoderLib/AnnexBwrite.h b/source/Lib/EncoderLib/AnnexBwrite.h index 2ba8c9a66cfd0ae04b04189710f4dcd4e10c97ec..3b49c99ba7e2eb1120c453e4c785865186e68e95 100644 --- a/source/Lib/EncoderLib/AnnexBwrite.h +++ b/source/Lib/EncoderLib/AnnexBwrite.h @@ -67,10 +67,18 @@ static std::vector<uint32_t> writeAnnexB(std::ostream& out, const AccessUnit& au #endif #else #if JVET_N0349_DPS +#if JVET_N0278_HLS + if (it == au.begin() || nalu.m_nalUnitType == NAL_UNIT_DPS || nalu.m_nalUnitType == NAL_UNIT_SPS || nalu.m_nalUnitType == NAL_UNIT_VPS || nalu.m_nalUnitType == NAL_UNIT_PPS) +#else if (it == au.begin() || nalu.m_nalUnitType == NAL_UNIT_DPS || nalu.m_nalUnitType == NAL_UNIT_SPS || nalu.m_nalUnitType == NAL_UNIT_PPS) +#endif +#else +#if JVET_N0278_HLS + if (it == au.begin() || nalu.m_nalUnitType == NAL_UNIT_SPS || nalu.m_nalUnitType == NAL_UNIT_VPS || nalu.m_nalUnitType == NAL_UNIT_PPS) #else if (it == au.begin() || nalu.m_nalUnitType == NAL_UNIT_SPS || nalu.m_nalUnitType == NAL_UNIT_PPS) #endif +#endif #endif { /* From AVC, When any of the following conditions are fulfilled, the diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h index 301dc5d5052cb74723d3c675b7d7c2a416ab0ae8..a1645296d4df56e28255e8d33356c2e29df3fda9 100644 --- a/source/Lib/EncoderLib/EncCfg.h +++ b/source/Lib/EncoderLib/EncCfg.h @@ -570,7 +570,7 @@ protected: CostMode m_costMode; ///< The cost function to use, primarily when considering lossless coding. -#if HEVC_VPS +#if HEVC_VPS || JVET_N0278_HLS VPS m_cVPS; #endif #if JVET_N0349_DPS @@ -1466,7 +1466,7 @@ public: CostMode getCostMode( ) const { return m_costMode; } void setCostMode(CostMode m ) { m_costMode = m; } -#if HEVC_VPS +#if HEVC_VPS || JVET_N0278_HLS void setVPS(VPS *p) { m_cVPS = *p; } VPS * getVPS() { return &m_cVPS; } #endif diff --git a/source/Lib/EncoderLib/EncGOP.cpp b/source/Lib/EncoderLib/EncGOP.cpp index e6d90fa960ca4119dca6a25ff817a732ff16cf3c..39ee29db7c5eb2eae79ff6c52c564ca12e2c039d 100644 --- a/source/Lib/EncoderLib/EncGOP.cpp +++ b/source/Lib/EncoderLib/EncGOP.cpp @@ -199,7 +199,7 @@ void EncGOP::init ( EncLib* pcEncLib ) m_pcReshaper = pcEncLib->getReshaper(); } -#if HEVC_VPS +#if HEVC_VPS || JVET_N0278_HLS int EncGOP::xWriteVPS (AccessUnit &accessUnit, const VPS *vps) { OutputNALUnit nalu(NAL_UNIT_VPS); @@ -261,7 +261,7 @@ int EncGOP::xWriteParameterSets (AccessUnit &accessUnit, Slice *slice, const boo { int actualTotalBits = 0; -#if HEVC_VPS +#if HEVC_VPS || JVET_N0278_HLS if (bSeqFirst) { actualTotalBits += xWriteVPS(accessUnit, m_pcEncLib->getVPS()); @@ -349,7 +349,7 @@ void EncGOP::xWriteLeadingSEIOrdered (SEIMessages& seiMessages, SEIMessages& duI while ( (itNalu!=accessUnit.end())&& ( (*itNalu)->m_nalUnitType==NAL_UNIT_ACCESS_UNIT_DELIMITER -#if HEVC_VPS +#if HEVC_VPS || JVET_N0278_HLS || (*itNalu)->m_nalUnitType==NAL_UNIT_VPS #endif #if JVET_N0349_DPS @@ -3405,10 +3405,18 @@ void EncGOP::xCalculateAddPSNR(Picture* pcPic, PelUnitBuf cPicD, const AccessUni #endif #else #if JVET_N0349_DPS +#if JVET_N0278_HLS + if (it == accessUnit.begin() || (*it)->m_nalUnitType == NAL_UNIT_VPS || (*it)->m_nalUnitType == NAL_UNIT_DPS || (*it)->m_nalUnitType == NAL_UNIT_SPS || (*it)->m_nalUnitType == NAL_UNIT_PPS) +#else if (it == accessUnit.begin() || (*it)->m_nalUnitType == NAL_UNIT_DPS || (*it)->m_nalUnitType == NAL_UNIT_SPS || (*it)->m_nalUnitType == NAL_UNIT_PPS) +#endif +#else +#if JVET_N0278_HLS + if (it == accessUnit.begin() || (*it)->m_nalUnitType == NAL_UNIT_VPS || (*it)->m_nalUnitType == NAL_UNIT_SPS || (*it)->m_nalUnitType == NAL_UNIT_PPS) #else if (it == accessUnit.begin() || (*it)->m_nalUnitType == NAL_UNIT_SPS || (*it)->m_nalUnitType == NAL_UNIT_PPS) #endif +#endif #endif { numRBSPBytes += 4; diff --git a/source/Lib/EncoderLib/EncGOP.h b/source/Lib/EncoderLib/EncGOP.h index 7316dae004c81e120857570afea3ce06a146c3a7..984c957daab29590b13625c93f7f4f25918eae8e 100644 --- a/source/Lib/EncoderLib/EncGOP.h +++ b/source/Lib/EncoderLib/EncGOP.h @@ -272,7 +272,7 @@ protected: void xWriteTrailingSEIMessages (SEIMessages& seiMessages, AccessUnit &accessUnit, int temporalId, const SPS *sps); void xWriteDuSEIMessages (SEIMessages& duInfoSeiMessages, AccessUnit &accessUnit, int temporalId, const SPS *sps, std::deque<DUData> &duData); -#if HEVC_VPS +#if HEVC_VPS || JVET_N0278_HLS int xWriteVPS (AccessUnit &accessUnit, const VPS *vps); #endif #if JVET_N0349_DPS diff --git a/source/Lib/EncoderLib/EncLib.cpp b/source/Lib/EncoderLib/EncLib.cpp index 36b23e4be172a912bb2ff96767fbd5e719811c98..fa1e0f612f8cf84a7098873fa28fbb6e2af5135b 100644 --- a/source/Lib/EncoderLib/EncLib.cpp +++ b/source/Lib/EncoderLib/EncLib.cpp @@ -240,6 +240,8 @@ void EncLib::init( bool isFieldCoding, AUWriterIf* auWriterIf ) xInitSPS(sps0); #if HEVC_VPS xInitVPS(m_cVPS, sps0); +#elif JVET_N0278_HLS + xInitVPS(m_cVPS); #endif #if JVET_N0349_DPS @@ -885,6 +887,17 @@ void EncLib::xInitVPS(VPS &vps, const SPS &sps) // Set up HrdParameters here. } } +#elif JVET_N0278_HLS +void EncLib::xInitVPS(VPS &vps) +{ + // The SPS must have already been set up. + // set the VPS profile information. + vps.setMaxLayers(1); + for (uint32_t i = 0; i < vps.getMaxLayers(); i++) + { + vps.setVPSIncludedLayerId(0, i); + } +} #endif #if JVET_N0349_DPS diff --git a/source/Lib/EncoderLib/EncLib.h b/source/Lib/EncoderLib/EncLib.h index 053a2d8cb1c2e76cc18c7231d9def07a20006f0a..cae48e238557583f9c1a2189aad4692f495fb801 100644 --- a/source/Lib/EncoderLib/EncLib.h +++ b/source/Lib/EncoderLib/EncLib.h @@ -154,6 +154,8 @@ protected: void xGetNewPicBuffer ( std::list<PelUnitBuf*>& rcListPicYuvRecOut, Picture*& rpcPic, int ppsId ); ///< get picture buffer which will be processed. If ppsId<0, then the ppsMap will be queried for the first match. #if HEVC_VPS void xInitVPS (VPS &vps, const SPS &sps); ///< initialize VPS from encoder options +#elif JVET_N0278_HLS + void xInitVPS (VPS &vps); ///< initialize VPS from encoder options #endif #if JVET_N0349_DPS void xInitDPS (DPS &dps, const SPS &sps, const int dpsId); ///< initialize DPS from encoder options diff --git a/source/Lib/EncoderLib/VLCWriter.cpp b/source/Lib/EncoderLib/VLCWriter.cpp index df4e84aab380c13e976269113c8862c62a969135..638b2076f6dfb0cf97a74b3b24571f0d45f5fc0d 100644 --- a/source/Lib/EncoderLib/VLCWriter.cpp +++ b/source/Lib/EncoderLib/VLCWriter.cpp @@ -1281,6 +1281,25 @@ void HLSWriter::codeVPS( const VPS* pcVPS ) //future extensions here.. xWriteRbspTrailingBits(); } +#elif JVET_N0278_HLS +void HLSWriter::codeVPS(const VPS* pcVPS) +{ +#if ENABLE_TRACING + xTraceVPSHeader(); +#endif + WRITE_CODE(pcVPS->getVPSId(), 4, "vps_video_parameter_set_id"); + WRITE_CODE(pcVPS->getMaxLayers() - 1, 8, "vps_max_layers_minus1"); + for (uint32_t i = 0; i <= pcVPS->getMaxLayers() - 1; i++) + { + WRITE_CODE(pcVPS->getVPSIncludedLayerId(i), 7, "vps_included_layer_id"); + WRITE_FLAG(0, "vps_reserved_zero_1bit"); + } + + WRITE_FLAG(0, "vps_extension_flag"); + + //future extensions here.. + xWriteRbspTrailingBits(); +} #endif void HLSWriter::codeSliceHeader ( Slice* pcSlice ) diff --git a/source/Lib/EncoderLib/VLCWriter.h b/source/Lib/EncoderLib/VLCWriter.h index 17e8d91d1e1fe43ecfff6f7edbb5553030596f3f..102fa6f837120dda75f3a28ce5ad1fb06252cbcd 100644 --- a/source/Lib/EncoderLib/VLCWriter.h +++ b/source/Lib/EncoderLib/VLCWriter.h @@ -126,7 +126,7 @@ public: void codeSPS ( const SPS* pcSPS ); void codePPS ( const PPS* pcPPS ); void codeAPS ( APS* pcAPS); -#if HEVC_VPS +#if HEVC_VPS || JVET_N0278_HLS void codeVPS ( const VPS* pcVPS ); #endif #if JVET_N0349_DPS