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