diff --git a/source/App/DecoderApp/DecApp.cpp b/source/App/DecoderApp/DecApp.cpp
index a1e1abf51dcd5f57323bc02391543750a22ab153..7ef205b856c9f01b64238b25a1723be2177ebce5 100644
--- a/source/App/DecoderApp/DecApp.cpp
+++ b/source/App/DecoderApp/DecApp.cpp
@@ -371,6 +371,12 @@ void DecApp::xCreateDecLib()
     std::ostream &os=m_seiMessageFileStream.is_open() ? m_seiMessageFileStream : std::cout;
     m_cDecLib.setDecodedSEIMessageOutputStream(&os);
   }
+#if JVET_S0257_DUMP_360SEI_MESSAGE
+  if (!m_outputDecoded360SEIMessagesFilename.empty())
+  {
+    m_cDecLib.setDecoded360SEIMessageFileName(m_outputDecoded360SEIMessagesFilename);
+  }
+#endif
   m_cDecLib.m_targetSubPicIdx = this->m_targetSubPicIdx;
   m_cDecLib.initScalingList();
 }
diff --git a/source/App/DecoderApp/DecAppCfg.cpp b/source/App/DecoderApp/DecAppCfg.cpp
index 602f949ab9ca2c16c60df75d173484ec5206b59b..6e95abb7490b528857241b043d8f8300eea2d7e3 100644
--- a/source/App/DecoderApp/DecAppCfg.cpp
+++ b/source/App/DecoderApp/DecAppCfg.cpp
@@ -97,6 +97,9 @@ bool DecAppCfg::parseCfg( int argc, char* argv[] )
   ("TarDecLayerIdSetFile,l",    cfg_TargetDecLayerIdSetFile,           string(""), "targetDecLayerIdSet file name. The file should include white space separated LayerId values to be decoded. Omitting the option or a value of -1 in the file decodes all layers.")
   ("SEIColourRemappingInfoFilename",  m_colourRemapSEIFileName,        string(""), "Colour Remapping YUV output file name. If empty, no remapping is applied (ignore SEI message)\n")
   ("OutputDecodedSEIMessagesFilename",  m_outputDecodedSEIMessagesFilename,    string(""), "When non empty, output decoded SEI messages to the indicated file. If file is '-', then output to stdout\n")
+#if JVET_S0257_DUMP_360SEI_MESSAGE
+  ("360DumpFile",  m_outputDecoded360SEIMessagesFilename, string(""), "When non empty, output decoded 360 SEI messages to the indicated file.\n")
+#endif
   ("ClipOutputVideoToRec709Range",      m_bClipOutputVideoToRec709Range,  false,   "If true then clip output video to the Rec. 709 Range on saving")
   ("PYUV",                      m_packedYUVMode,                       false,      "If true then output 10-bit and 12-bit YUV data as 5-byte and 3-byte (respectively) packed YUV data. Ignored for interlaced output.")
 #if ENABLE_TRACING
@@ -235,6 +238,9 @@ DecAppCfg::DecAppCfg()
 , m_colourRemapSEIFileName()
 , m_targetDecLayerIdSet()
 , m_outputDecodedSEIMessagesFilename()
+#if JVET_S0257_DUMP_360SEI_MESSAGE
+, m_outputDecoded360SEIMessagesFilename()
+#endif
 , m_bClipOutputVideoToRec709Range(false)
 , m_packedYUVMode(false)
 , m_statMode(0)
diff --git a/source/App/DecoderApp/DecAppCfg.h b/source/App/DecoderApp/DecAppCfg.h
index f28b07d77d951287f0e67544dc8cda3fd9bb9e02..607425be31a2556128d12fc0d9c193bdd24121b7 100644
--- a/source/App/DecoderApp/DecAppCfg.h
+++ b/source/App/DecoderApp/DecAppCfg.h
@@ -72,6 +72,9 @@ protected:
   std::string   m_colourRemapSEIFileName;             ///< output Colour Remapping file name
   std::vector<int> m_targetDecLayerIdSet;             ///< set of LayerIds to be included in the sub-bitstream extraction process.
   std::string   m_outputDecodedSEIMessagesFilename;   ///< filename to output decoded SEI messages to. If '-', then use stdout. If empty, do not output details.
+#if JVET_S0257_DUMP_360SEI_MESSAGE
+  std::string   m_outputDecoded360SEIMessagesFilename;   ///< filename to output decoded 360 SEI messages to.
+#endif
 
 
   bool          m_bClipOutputVideoToRec709Range;      ///< If true, clip the output video to the Rec 709 range on saving.
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index 8057ce0ed8a90685e60f2090f8e0573ce6f44ce4..4b134e03c686346cb7ad0bae940fdfe136b447cf 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -50,7 +50,6 @@
 #include <assert.h>
 #include <cassert>
 
-
 //########### place macros to be removed in next cycle below this line ###############
 #define JVET_S0074_SPS_REORDER                            1 // JVET-S0074: aspect 1, rearrange some syntax elements in SPS
 
@@ -60,6 +59,8 @@
 
 
 //########### place macros to be be kept below this line ###############
+#define JVET_S0257_DUMP_360SEI_MESSAGE                    1 // Software support of 360 SEI messages
+
 #define JVET_R0351_HIGH_BIT_DEPTH_SUPPORT                 1 // JVET-R0351: high bit depth coding support (syntax changes, no mathematical differences for CTCs)
 #define JVET_R0351_HIGH_BIT_DEPTH_ENABLED                 0 // JVET-R0351: high bit depth coding enabled (increases accuracies of some calculations, e.g. transforms)
 
diff --git a/source/Lib/DecoderLib/DecLib.cpp b/source/Lib/DecoderLib/DecLib.cpp
index 754e86dd1d350f5e64641794026451121774bec0..3fe5ead148584045af796b1f0b18d05445e2bd39 100644
--- a/source/Lib/DecoderLib/DecLib.cpp
+++ b/source/Lib/DecoderLib/DecLib.cpp
@@ -436,6 +436,9 @@ DecLib::DecLib()
   , m_sliceLmcsApsId(-1)
   , m_pDecodedSEIOutputStream(NULL)
   , m_audIrapOrGdrAuFlag( false )
+#if JVET_S0257_DUMP_360SEI_MESSAGE
+  , m_decoded360SeiDumpFileName()
+#endif
   , m_decodedPictureHashSEIEnabled(false)
   , m_numberOfChecksumErrorsDetected(0)
   , m_warningMessageSkipPicture(false)
@@ -1780,6 +1783,9 @@ void DecLib::xParsePrefixSEImessages()
     const SPS *sps = m_parameterSetManager.getActiveSPS();
     const VPS *vps = m_parameterSetManager.getVPS(sps->getVPSId());
     m_seiReader.parseSEImessage( &(nalu.getBitstream()), m_SEIs, nalu.m_nalUnitType, nalu.m_nuhLayerId, nalu.m_temporalId, vps, sps, m_HRD, m_pDecodedSEIOutputStream );
+#if JVET_S0257_DUMP_360SEI_MESSAGE
+    m_seiCfgDump.write360SeiDump( m_decoded360SeiDumpFileName, m_SEIs, sps );
+#endif
     m_accessUnitSeiPayLoadTypes.push_back(std::tuple<NalUnitType, int, SEI::PayloadType>(nalu.m_nalUnitType, nalu.m_nuhLayerId, m_SEIs.back()->payloadType()));
     delete m_prefixSEINALUs.front();
     m_prefixSEINALUs.pop_front();
@@ -2693,6 +2699,9 @@ bool DecLib::decode(InputNALUnit& nalu, int& iSkipFrame, int& iPOCLastDisplay, i
         const SPS *sps = m_parameterSetManager.getActiveSPS();
         const VPS *vps = m_parameterSetManager.getVPS(sps->getVPSId());
         m_seiReader.parseSEImessage( &(nalu.getBitstream()), m_pcPic->SEIs, nalu.m_nalUnitType, nalu.m_nuhLayerId, nalu.m_temporalId, vps, sps, m_HRD, m_pDecodedSEIOutputStream );
+#if JVET_S0257_DUMP_360SEI_MESSAGE
+        m_seiCfgDump.write360SeiDump(m_decoded360SeiDumpFileName, m_pcPic->SEIs, sps);
+#endif
         m_accessUnitSeiPayLoadTypes.push_back(std::tuple<NalUnitType, int, SEI::PayloadType>(nalu.m_nalUnitType, nalu.m_nuhLayerId, m_pcPic->SEIs.back()->payloadType()));
       }
       else
diff --git a/source/Lib/DecoderLib/DecLib.h b/source/Lib/DecoderLib/DecLib.h
index bb721bfca5732bdd884607a8d8b3ae992fa32dae..6202c7de30e74073ef28cceb00e083d1e969552b 100644
--- a/source/Lib/DecoderLib/DecLib.h
+++ b/source/Lib/DecoderLib/DecLib.h
@@ -105,6 +105,9 @@ private:
   HLSyntaxReader          m_HLSReader;
   CABACDecoder            m_CABACDecoder;
   SEIReader               m_seiReader;
+#if JVET_S0257_DUMP_360SEI_MESSAGE
+  SeiCfgFileDump          m_seiCfgDump;
+#endif
   LoopFilter              m_cLoopFilter;
   SampleAdaptiveOffset    m_cSAO;
   AdaptiveLoopFilter      m_cALF;
@@ -135,6 +138,9 @@ private:
   int                     m_sliceLmcsApsId;         //value of LmcsApsId, constraint is same id for all slices in one picture
   std::ostream           *m_pDecodedSEIOutputStream;
   uint32_t                m_audIrapOrGdrAuFlag;
+#if JVET_S0257_DUMP_360SEI_MESSAGE
+  std::string             m_decoded360SeiDumpFileName;
+#endif
 
   int                     m_decodedPictureHashSEIEnabled;  ///< Checksum(3)/CRC(2)/MD5(1)/disable(0) acting on decoded picture hash SEI message
   uint32_t                m_numberOfChecksumErrorsDetected;
@@ -222,6 +228,9 @@ public:
   bool  getFirstSliceInSequence(int layerId) const { return m_firstSliceInSequence[layerId]; }
   void  setFirstSliceInSequence(bool val, int layerId) { m_firstSliceInSequence[layerId] = val; }
   void  setDecodedSEIMessageOutputStream(std::ostream *pOpStream) { m_pDecodedSEIOutputStream = pOpStream; }
+#if JVET_S0257_DUMP_360SEI_MESSAGE
+  void  setDecoded360SEIMessageFileName(std::string &Dump360SeiFileName) { m_decoded360SeiDumpFileName = Dump360SeiFileName; }
+#endif
   uint32_t  getNumberOfChecksumErrorsDetected() const { return m_numberOfChecksumErrorsDetected; }
 
   int  getDebugCTU( )               const { return m_debugCTU; }
diff --git a/source/Lib/DecoderLib/SEIread.cpp b/source/Lib/DecoderLib/SEIread.cpp
index 363214df267f509bacd25f2cba364d18ee8916dd..29c5c310e7c6f7be88b37b2c12c568d461d70eee 100644
--- a/source/Lib/DecoderLib/SEIread.cpp
+++ b/source/Lib/DecoderLib/SEIread.cpp
@@ -1390,6 +1390,146 @@ void SEIReader::xParseSEISampleAspectRatioInfo(SEISampleAspectRatioInfo& sei, ui
   }
 }
 
+#if JVET_S0257_DUMP_360SEI_MESSAGE
+void SeiCfgFileDump::write360SeiDump (std::string decoded360MessageFileName, SEIMessages& seis, const SPS* sps)
+{
+  if (m_360SEIMessageDumped)
+  {
+    return;
+  }
+
+  SEIMessages equirectangularProjectionSEIs = getSeisByType(seis, SEI::EQUIRECTANGULAR_PROJECTION);
+  if (!equirectangularProjectionSEIs.empty())
+  {
+    SEIEquirectangularProjection* sei = (SEIEquirectangularProjection*)equirectangularProjectionSEIs.front();
+    xDumpSEIEquirectangularProjection(*sei, sps, decoded360MessageFileName);
+    m_360SEIMessageDumped = true;
+  }
+  else
+  {
+    SEIMessages generalizedCubemapProjectionSEIs = getSeisByType(seis, SEI::GENERALIZED_CUBEMAP_PROJECTION);
+    if (!generalizedCubemapProjectionSEIs.empty())
+    {
+      SEIGeneralizedCubemapProjection* sei = (SEIGeneralizedCubemapProjection*)generalizedCubemapProjectionSEIs.front();
+      xDumpSEIGeneralizedCubemapProjection(*sei, sps, decoded360MessageFileName);
+      m_360SEIMessageDumped = true; 
+    }
+  }
+}
+
+void SeiCfgFileDump::xDumpSEIEquirectangularProjection     (SEIEquirectangularProjection &sei, const SPS* sps, std::string decoded360MessageFileName)
+{
+  if (!decoded360MessageFileName.empty())
+  {
+    FILE *fp = fopen(decoded360MessageFileName.c_str(), "w");
+    if (fp)
+    {
+      int chromaFormatTable[4] = {400, 420, 422, 444};
+      fprintf(fp, "InputBitDepth                 : %d    # Input bitdepth\n", sps->getBitDepth(CHANNEL_TYPE_LUMA));
+      fprintf(fp, "InputChromaFormat             : %d    # Ratio of luminance to chrominance samples\n", chromaFormatTable[sps->getChromaFormatIdc()]);
+      fprintf(fp, "SourceWidth                   : %d    # Input  frame width\n", sps->getMaxPicWidthInLumaSamples());
+      fprintf(fp, "SourceHeight                  : %d    # Input  frame height\n\n", sps->getMaxPicHeightInLumaSamples());
+
+      fprintf(fp, "InputGeometryType             : 0     # 0: equirectangular; 1: cubemap; 2: equalarea; this should be in the cfg of per sequence.\n");
+      if (sei.m_erpGuardBandFlag == 1)
+      {
+        fprintf(fp, "InputPERP                     : 1     # 0: original ERP input; 1: padded ERP input\n");
+        fprintf(fp, "CodingPERP                    : 0     # 0: coding with original ERP size; 1: coding with padded ERP\n");
+      }
+      fclose(fp);
+      m_360SEIMessageDumped = true;
+    }
+    else
+    {
+      msg( ERROR, "File %s could not be opened.\n", decoded360MessageFileName.c_str() );
+    }
+  }
+}
+void SeiCfgFileDump::xDumpSEIGeneralizedCubemapProjection  (SEIGeneralizedCubemapProjection &sei, const SPS* sps, std::string decoded360MessageFileName)
+{
+  if (!sei.m_gcmpCancelFlag)
+  {
+    int numFace = sei.m_gcmpPackingType == 4 || sei.m_gcmpPackingType == 5 ? 5 : 6;
+    int packingTypeTable[6][2] = {{6, 1}, {3, 2}, {2, 3}, {1, 6}, {1, 5}, {5, 1}};
+    int rotationTable[4] = {0, 90, 180, 270};
+    std::string packingTypeStr = "";
+    std::string gcmpsettingsStr = "";
+    std::ostringstream oss;
+
+    packingTypeStr += "SourceFPStructure                 : " + std::to_string(packingTypeTable[sei.m_gcmpPackingType][0]) + " " + std::to_string(packingTypeTable[sei.m_gcmpPackingType][1]);
+    gcmpsettingsStr += "InputGCMPSettings                 : ";
 
+    for (int i = 0; i < numFace; i++)
+    {
+      int rotation = rotationTable[sei.m_gcmpFaceRotation[i]];
+      if (sei.m_gcmpFaceIndex[i] == 1)
+      {
+        rotation = (rotation + 270) % 360 + 360;
+      }
+      else if (sei.m_gcmpFaceIndex[i] == 2)
+      {
+        rotation = (rotation + 180) % 360 + 360;
+      }
+      else
+      {
+        rotation += 360;
+      }
+      if (i % packingTypeTable[sei.m_gcmpPackingType][1] == 0)
+      {
+        packingTypeStr += "   ";
+      }
+      packingTypeStr += std::to_string(sei.m_gcmpFaceIndex[i]) + " " + std::to_string(rotation) + " ";
+
+      if (sei.m_gcmpMappingFunctionType == 2)
+      {
+        double a = ((int)sei.m_gcmpFunctionCoeffU[i] + 1) / 128.0;
+        double b = ((int)sei.m_gcmpFunctionCoeffV[i] + 1) / 128.0;
+        oss.str("");
+        oss<<a;
+        std::string a_str = oss.str();
+        oss.str("");
+        oss<<b;
+        std::string b_str = oss.str();
+        gcmpsettingsStr += a_str + " " + std::to_string(sei.m_gcmpFunctionUAffectedByVFlag[i]) + " " + b_str + " " + std::to_string(sei.m_gcmpFunctionVAffectedByUFlag[i]) + "   ";
+      }
+    }
+    if (!decoded360MessageFileName.empty())
+    {
+      FILE *fp = fopen(decoded360MessageFileName.c_str(), "w");
+      if (fp)
+      {
+        int chromaFormatTable[4] = {400, 420, 422, 444};
+        fprintf(fp, "InputBitDepth                 : %d    # Input bitdepth\n", sps->getBitDepth(CHANNEL_TYPE_LUMA));
+        fprintf(fp, "InputChromaFormat             : %d    # Ratio of luminance to chrominance samples\n", chromaFormatTable[sps->getChromaFormatIdc()]);
+        fprintf(fp, "SourceWidth                   : %d    # Input  frame width\n", sps->getMaxPicWidthInLumaSamples());
+        fprintf(fp, "SourceHeight                  : %d    # Input  frame height\n\n", sps->getMaxPicHeightInLumaSamples());
+
+        fprintf(fp, "InputGeometryType             : 15    # 0: equirectangular; 1: cubemap; 2: equalarea; this should be in the cfg of per sequence.\n");
+
+        packingTypeStr += " # frame packing order: numRows numCols Row0Idx0 ROT Row0Idx1 ROT ... Row1...";
+        gcmpsettingsStr += " # mapping function parameters for each face: u coefficient, u affected by v flag, v coefficient, v affected by u flag";
+        fprintf(fp, "%s\n", packingTypeStr.c_str());
+        fprintf(fp, "InputGCMPMappingType              : %d                                    # 0: CMP; 1: EAC; 2: parameterized CMP\n", (int)sei.m_gcmpMappingFunctionType);
+        if ((int)sei.m_gcmpMappingFunctionType == 2)
+          fprintf(fp, "%s\n", gcmpsettingsStr.c_str());
+        fprintf(fp, "InputGCMPPaddingFlag              : %d                                   # 0: input without guard bands; 1: input with guard bands\n", sei.m_gcmpGuardBandFlag);
+        if (sei.m_gcmpGuardBandFlag)
+        {
+          fprintf(fp, "InputGCMPPaddingType              : %d                                   # 0: unspecified(repetitive padding is used); 1: repetitive padding; 2: copy from neighboring face; 3: geometry padding\n", (int)sei.m_gcmpGuardBandType);
+          fprintf(fp, "InputGCMPPaddingExteriorFlag      : %d                                   # 0: guard bands only on discontinuous edges; 1: guard bands on both discontinuous edges and frame boundaries\n", sei.m_gcmpGuardBandBoundaryExteriorFlag);
+          fprintf(fp, "InputGCMPPaddingSize              : %d                                   # guard band size for input GCMP\n", (int)sei.m_gcmpGuardBandSamplesMinus1 + 1);
+        }
+        fclose(fp);
+        m_360SEIMessageDumped = true;
+      }
+      else
+      {
+        msg( ERROR, "File %s could not be opened.\n", decoded360MessageFileName.c_str() );
+      }
+    }
+  }
+}
+
+#endif
 
 //! \}
diff --git a/source/Lib/DecoderLib/SEIread.h b/source/Lib/DecoderLib/SEIread.h
index 140a064deac148c8901a54592a47e6bfc2a99baa..69532e758827f264fd245a069043fe080a47593d 100644
--- a/source/Lib/DecoderLib/SEIread.h
+++ b/source/Lib/DecoderLib/SEIread.h
@@ -96,6 +96,27 @@ protected:
   HRD m_nestedHrd;
 };
 
+#if JVET_S0257_DUMP_360SEI_MESSAGE
+class SeiCfgFileDump
+{
+public:
+  SeiCfgFileDump()
+  : m_360SEIMessageDumped(false)
+  {};
+  virtual ~SeiCfgFileDump() {};
+
+  void write360SeiDump (std::string decoded360MessageFileName, SEIMessages& seis, const SPS* sps);
+
+protected:
+  void xDumpSEIEquirectangularProjection     (SEIEquirectangularProjection &sei, const SPS* sps, std::string decoded360MessageFileName);
+  void xDumpSEIGeneralizedCubemapProjection  (SEIGeneralizedCubemapProjection &sei, const SPS* sps, std::string decoded360MessageFileName);
+
+  bool m_360SEIMessageDumped;
+
+};
+
+
+#endif
 
 //! \}