diff --git a/doc/software-manual.tex b/doc/software-manual.tex
index e7d15072c8166c81486865daa1cf4a670591e87a..0b9e860dfaf0f79c7c7f2efe7905924f1e6543fe 100644
--- a/doc/software-manual.tex
+++ b/doc/software-manual.tex
@@ -3524,23 +3524,30 @@ Enables or disables the use of adaptive color transform (ACT).
 
 \Option{HorCollocatedChroma} &
 %\ShortOption{\None} &
-\Default{true} &
-Specifies location of a chroma sample relatively to the luma sample in horizontal direction in the reference picture resampling.
+\Default{-1} &
+Specifies location of top-left chroma sample relative to top-left luma sample in horizontal direction for reference picture resampling.
+For chroma formats other than 4:2:0, the value defaults to 1.
+When ChromaSampleLocType is equal to 6 (unspecified) and HorCollocatedChroma is equal to -1, the value defaults to 1.
 \par
 \begin{tabular}{cp{0.45\textwidth}}
-	0 & horizontally shifted by 0.5 units of luma samples.\\
-	1 & collocated (default). \\
+	-1 & value based on ChromaSampleLocType (default)\\
+	0 & horizontally shifted by 0.5 units of luma samples\\
+	1 & collocated \\
 \end{tabular}
 \\
 
 \Option{VerCollocatedChroma} &
 %\ShortOption{\None} &
-\Default{false} &
-Specifies location of a chroma sample relatively to the luma sample in vertical direction in the cross-component linear model intra prediction and the reference picture resampling.
+\Default{-1} &
+Specifies location of top-left chroma sample relative to top-left luma sample in vertical direction for cross-component linear model (CCLM)
+intra prediction and for reference picture resampling.
+For chroma formats other than 4:2:0, the value defaults to 1.
+When ChromaSampleLocType is equal to 6 (unspecified) and VerCollocatedChroma is equal to -1, the value defaults to 0.
 \par
 \begin{tabular}{cp{0.45\textwidth}}
-	0 & vertically shifted by 0.5 units of luma samples (default).\\
-	1 & collocated. \\
+	-1 & value based on ChromaSampleLocType (default)\\
+	0 & vertically shifted by 0.5 units of luma samples\\
+	1 & collocated\\
 \end{tabular}
 \\
 
@@ -3800,16 +3807,20 @@ Specifies the value of general_non_projected_constraint_flag
 \\
 \Option{ChromaLocInfoPresent} &
 \Default{false} &
-Signals whether chroma_sample_loc_type_top_field and chroma_sample_loc_type_bottom_field are present.
+Signals whether chroma_sample_loc_type_top_field, chroma_sample_loc_type_bottom_field and chroma_sample_loc_type are present.
 \\
 \Option{ChromaSampleLocTypeTopField} &
-\Default{0} &
+\Default{6 (Unspecified)} &
 Specifies the location of chroma samples for top field.
 \\
 \Option{ChromaSampleLocTypeBottomField} &
-\Default{0} &
+\Default{6 (Unspecified)} &
 Specifies the location of chroma samples for bottom field.
 \\
+\Option{ChromaSampleLocType} &
+\Default{6 (Unspecified)} &
+Specifies the location of chroma samples for frame.
+\\
 \end{OptionTableNoShorthand}
 
 
diff --git a/source/App/DecoderApp/DecApp.cpp b/source/App/DecoderApp/DecApp.cpp
index c00f4c8a79dccff841c58d44a324c59cea7ffaa2..46d77124c210b4f1b12287097e66ef513d2d255f 100644
--- a/source/App/DecoderApp/DecApp.cpp
+++ b/source/App/DecoderApp/DecApp.cpp
@@ -487,7 +487,8 @@ uint32_t DecApp::decode()
               const int picWidth = pps->getPicWidthInLumaSamples() - (confWindow.getWindowLeftOffset() + confWindow.getWindowRightOffset()) * sx;
               const int picHeight = pps->getPicHeightInLumaSamples() - (confWindow.getWindowTopOffset() + confWindow.getWindowBottomOffset()) * sy;
               m_cVideoIOYuvReconFile[nalu.m_nuhLayerId].setOutputY4mInfo(
-                picWidth, picHeight, frameRate, layerOutputBitDepth[ChannelType::LUMA], sps->getChromaFormatIdc());
+                picWidth, picHeight, frameRate, layerOutputBitDepth[ChannelType::LUMA], sps->getChromaFormatIdc(),
+                sps->getVuiParameters()->getChromaSampleLocType());
             }
             m_cVideoIOYuvReconFile[nalu.m_nuhLayerId].open(reconFileName, true, layerOutputBitDepth,
                                                            layerOutputBitDepth, bitDepths);   // write mode
diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp
index a75328360977618bf4023c6f98361181ce677062..a8289ee1f846d8606a28dac310c717437f62fe74 100644
--- a/source/App/EncoderApp/EncApp.cpp
+++ b/source/App/EncoderApp/EncApp.cpp
@@ -733,8 +733,8 @@ void EncApp::xInitLibCfg( int layerIdx )
   m_cEncLib.setPROF                                              ( m_PROF );
   m_cEncLib.setBIO                                               (m_BIO);
   m_cEncLib.setUseLMChroma                                       ( m_LMChroma );
-  m_cEncLib.setHorCollocatedChromaFlag                           ( m_horCollocatedChromaFlag );
-  m_cEncLib.setVerCollocatedChromaFlag                           ( m_verCollocatedChromaFlag );
+  m_cEncLib.setHorCollocatedChromaFlag(m_horCollocatedChromaFlag != 0);
+  m_cEncLib.setVerCollocatedChromaFlag(m_verCollocatedChromaFlag != 0);
   m_cEncLib.setExplicitMtsIntraEnabled((m_mtsMode & 1) != 0);
   m_cEncLib.setExplicitMtsInterEnabled((m_mtsMode & 2) != 0);
   m_cEncLib.setMTSIntraMaxCand                                   ( m_MTSIntraMaxCand );
@@ -1493,9 +1493,9 @@ void EncApp::xCreateLib( std::list<PelUnitBuf*>& recBufList, const int layerId )
     {
       const auto sx = SPS::getWinUnitX(m_chromaFormatIdc);
       const auto sy = SPS::getWinUnitY(m_chromaFormatIdc);
-      m_cVideoIOYuvReconFile.setOutputY4mInfo(m_sourceWidth - (m_confWinLeft + m_confWinRight) * sx,
-                                              m_sourceHeight - (m_confWinTop + m_confWinBottom) * sy, m_frameRate,
-                                              m_internalBitDepth[ChannelType::LUMA], m_chromaFormatIdc);
+      m_cVideoIOYuvReconFile.setOutputY4mInfo(
+        m_sourceWidth - (m_confWinLeft + m_confWinRight) * sx, m_sourceHeight - (m_confWinTop + m_confWinBottom) * sy,
+        m_frameRate, m_internalBitDepth[ChannelType::LUMA], m_chromaFormatIdc, m_chromaSampleLocType);
     }
     m_cVideoIOYuvReconFile.open( reconFileName, true, m_outputBitDepth, m_outputBitDepth, m_internalBitDepth );  // write mode
   }
@@ -1734,7 +1734,9 @@ bool EncApp::encodePrep( bool& eos )
     
     bool downsampling = (m_sourceWidthBeforeScale > m_sourceWidth) || (m_sourceHeightBeforeScale > m_sourceHeight);
     bool useLumaFilter = downsampling;
-    Picture::rescalePicture(scalingRatio, *m_orgPicBeforeScale, Window(), *m_orgPic, conformanceWindow1, m_inputChromaFormatIDC , m_internalBitDepth,useLumaFilter,downsampling,m_horCollocatedChromaFlag,m_verCollocatedChromaFlag );
+    Picture::rescalePicture(scalingRatio, *m_orgPicBeforeScale, Window(), *m_orgPic, conformanceWindow1,
+                            m_inputChromaFormatIDC, m_internalBitDepth, useLumaFilter, downsampling,
+                            m_horCollocatedChromaFlag != 0, m_verCollocatedChromaFlag != 0);
     m_trueOrgPic->copyFrom(*m_orgPic);
   }
   else
diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp
index 259f0e711a9b65a1c3f5d6207065af01f577fd59..0147fd8937097dff73c49ccb0904637c523f2846 100644
--- a/source/App/EncoderApp/EncAppCfg.cpp
+++ b/source/App/EncoderApp/EncAppCfg.cpp
@@ -776,6 +776,10 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
 
   bool sdr = false;
 
+  int chromaSampleLocType;
+  int chromaSampleLocTypeTopField;
+  int chromaSampleLocTypeBottomField;
+
   // clang-format off
   po::Options opts;
   opts.addOptions()
@@ -1014,10 +1018,12 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
   ("LMChroma",                                        m_LMChroma,                                           1, " LMChroma prediction "
                                                                                                                "\t0:  Disable LMChroma\n"
                                                                                                                "\t1:  Enable LMChroma\n")
-  ("HorCollocatedChroma",                             m_horCollocatedChromaFlag,                         true, "Specifies location of a chroma sample relatively to the luma sample in horizontal direction in the reference picture resampling\n"
+  ("HorCollocatedChroma",                             m_horCollocatedChromaFlag,                           -1, "Specifies location of a chroma sample relatively to the luma sample in horizontal direction in the reference picture resampling\n"
+                                                                                                               "\t-1: set according to chroma location type (default)\n"
                                                                                                                "\t0:  horizontally shifted by 0.5 units of luma samples\n"
-                                                                                                               "\t1:  collocated (default)\n")
-  ("VerCollocatedChroma",                             m_verCollocatedChromaFlag,                        false, "Specifies location of a chroma sample relatively to the luma sample in vertical direction in the cross-component linear model intra prediction and the reference picture resampling\n"
+                                                                                                               "\t1:  collocated\n")
+  ("VerCollocatedChroma",                             m_verCollocatedChromaFlag,                           -1, "Specifies location of a chroma sample relatively to the luma sample in vertical direction in the cross-component linear model intra prediction and the reference picture resampling\n"
+                                                                                                               "\t-1: set according to chroma location type (default)\n"
                                                                                                                "\t0:  horizontally co-sited, vertically shifted by 0.5 units of luma samples\n"
                                                                                                                "\t1:  collocated\n")
   ("MTS",                                             m_mtsMode,                                            0, "Multiple Transform Set (MTS)\n"
@@ -1337,9 +1343,9 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
   ("NonPackedSourceConstraintFlag",                   m_nonPackedConstraintFlag,                        false, "Indicate that source does not contain frame packing")
   ("NonProjectedConstraintFlag",                      m_nonProjectedConstraintFlag,                     false, "Indicate that the bitstream contains projection SEI messages")
   ("ChromaLocInfoPresent",                            m_chromaLocInfoPresentFlag,                       false, "Signals whether chroma_sample_loc_type_top_field and chroma_sample_loc_type_bottom_field are present")
-  ("ChromaSampleLocTypeTopField",                     m_chromaSampleLocTypeTopField,                        0, "Specifies the location of chroma samples for top field")
-  ("ChromaSampleLocTypeBottomField",                  m_chromaSampleLocTypeBottomField,                     0, "Specifies the location of chroma samples for bottom field")
-  ("ChromaSampleLocType",                             m_chromaSampleLocType,                                0, "Specifies the location of chroma samples for progressive content")
+  ("ChromaSampleLocTypeTopField",                     chromaSampleLocTypeTopField,    static_cast<int>(Chroma420LocType::UNSPECIFIED), "Specifies the location of chroma samples for top field")
+  ("ChromaSampleLocTypeBottomField",                  chromaSampleLocTypeBottomField, static_cast<int>(Chroma420LocType::UNSPECIFIED), "Specifies the location of chroma samples for bottom field")
+  ("ChromaSampleLocType",                             chromaSampleLocType,            static_cast<int>(Chroma420LocType::UNSPECIFIED), "Specifies the location of chroma samples for progressive content")
   ("OverscanInfoPresent",                             m_overscanInfoPresentFlag,                        false, "Indicates whether conformant decoded pictures are suitable for display using overscan\n")
   ("OverscanAppropriate",                             m_overscanAppropriateFlag,                        false, "Indicates whether conformant decoded pictures are suitable for display using overscan\n")
   ("VideoFullRange",                                  m_videoFullRangeFlag,                             false, "Indicates the black level and range of luma and chroma signals");
@@ -2477,6 +2483,11 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
     }
   }
 
+  // TODO: check whether values are within valid range
+  m_chromaSampleLocType            = static_cast<Chroma420LocType>(chromaSampleLocType);
+  m_chromaSampleLocTypeTopField    = static_cast<Chroma420LocType>(chromaSampleLocTypeTopField);
+  m_chromaSampleLocTypeBottomField = static_cast<Chroma420LocType>(chromaSampleLocTypeBottomField);
+
   if (isY4mFileExt(m_inputFileName))
   {
     int          width          = 0;
@@ -2484,10 +2495,13 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
     Fraction     frameRate;
     int          inputBitDepth  = 0;
     ChromaFormat chromaFormat = ChromaFormat::_420;
+    Chroma420LocType locType        = Chroma420LocType::UNSPECIFIED;
+
     VideoIOYuv   inputFile;
-    inputFile.parseY4mFileHeader(m_inputFileName, width, height, frameRate, inputBitDepth, chromaFormat);
+    inputFile.parseY4mFileHeader(m_inputFileName, width, height, frameRate, inputBitDepth, chromaFormat, locType);
     if (width != m_sourceWidth || height != m_sourceHeight || frameRate != m_frameRate
-        || inputBitDepth != m_inputBitDepth[ChannelType::LUMA] || chromaFormat != m_chromaFormatIdc)
+        || inputBitDepth != m_inputBitDepth[ChannelType::LUMA] || chromaFormat != m_chromaFormatIdc
+        || locType != m_chromaSampleLocType)
     {
       msg(WARNING, "\nWarning: Y4M file info is different from input setting. Using the info from Y4M file\n");
       m_sourceWidth            = width;
@@ -2496,6 +2510,14 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
       m_inputBitDepth.fill(inputBitDepth);
       m_chromaFormatIdc        = chromaFormat;
       m_msbExtendedBitDepth    = m_inputBitDepth;
+      m_chromaSampleLocType    = locType;
+    }
+
+    m_progressiveSourceFlag = true;   // TODO: update when processing of interlaced y4m files is supported
+    if (m_chromaFormatIdc == ChromaFormat::_420 && m_chromaSampleLocType != Chroma420LocType::UNSPECIFIED)
+    {
+      m_chromaLocInfoPresentFlag = true;
+      m_vuiParametersPresentFlag = true;
     }
   }
 
@@ -2884,17 +2906,58 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
 
   if (m_chromaFormatIdc != ChromaFormat::_420)
   {
-    if (!m_horCollocatedChromaFlag)
+    if (m_horCollocatedChromaFlag != 1)
     {
-      msg(WARNING, "\nWARNING: HorCollocatedChroma is forced to 1 for chroma formats other than 4:2:0\n");
-      m_horCollocatedChromaFlag = true;
+      if (m_horCollocatedChromaFlag == 0)
+      {
+        msg(WARNING, "WARNING: HorCollocatedChroma forced to 1 (chroma format is not 4:2:0)\n");
+      }
+      m_horCollocatedChromaFlag = 1;
     }
-    if (!m_verCollocatedChromaFlag)
+    if (m_verCollocatedChromaFlag != 1)
     {
-      msg(WARNING, "\nWARNING: VerCollocatedChroma is forced to 1 for chroma formats other than 4:2:0\n");
-      m_verCollocatedChromaFlag = true;
+      if (m_verCollocatedChromaFlag == 0)
+      {
+        msg(WARNING, "WARNING: VerCollocatedChroma is forced to 1 (chroma format is not 4:2:0)\n");
+      }
+      m_verCollocatedChromaFlag = 1;
     }
   }
+  else
+  {
+    if (m_horCollocatedChromaFlag == -1)
+    {
+      if (m_chromaSampleLocType != Chroma420LocType::UNSPECIFIED)
+      {
+        m_horCollocatedChromaFlag = m_chromaSampleLocType == Chroma420LocType::LEFT
+                                        || m_chromaSampleLocType == Chroma420LocType::TOP_LEFT
+                                        || m_chromaSampleLocType == Chroma420LocType::BOTTOM_LEFT
+                                      ? 1
+                                      : 0;
+      }
+      else
+      {
+        m_horCollocatedChromaFlag = 1;
+      }
+    }
+
+    if (m_verCollocatedChromaFlag == -1)
+    {
+      if (m_chromaSampleLocType != Chroma420LocType::UNSPECIFIED)
+      {
+        m_verCollocatedChromaFlag =
+          m_chromaSampleLocType == Chroma420LocType::TOP_LEFT || m_chromaSampleLocType == Chroma420LocType::TOP ? 1 : 0;
+      }
+      else
+      {
+        m_verCollocatedChromaFlag = 0;
+      }
+    }
+  }
+
+  CHECK(m_verCollocatedChromaFlag != 0 && m_verCollocatedChromaFlag != 1, "m_verCollocatedChromaFlag should be 0 or 1");
+  CHECK(m_horCollocatedChromaFlag != 0 && m_horCollocatedChromaFlag != 1, "m_horCollocatedChromaFlag should be 0 or 1");
+
 #if JVET_O0756_CONFIG_HDRMETRICS && !JVET_O0756_CALCULATE_HDRMETRICS
   if ( m_calculateHdrMetrics == true)
   {
diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h
index 2cf5adbcadc222b3ec5db98b4ed1bf9784491107..93783a50533d02fbaab150b2e1a54cda6c9b0b65 100644
--- a/source/App/EncoderApp/EncAppCfg.h
+++ b/source/App/EncoderApp/EncAppCfg.h
@@ -354,8 +354,8 @@ protected:
   bool      m_PROF;
   bool      m_BIO;
   int       m_LMChroma;
-  bool      m_horCollocatedChromaFlag;
-  bool      m_verCollocatedChromaFlag;
+  int                   m_horCollocatedChromaFlag;
+  int                   m_verCollocatedChromaFlag;
 
   int       m_mtsMode;                                        ///< XZ: Multiple Transform Set
   int       m_MTSIntraMaxCand;                                ///< XZ: Number of additional candidates to test
@@ -880,9 +880,9 @@ protected:
   bool      m_progressiveSourceFlag;                          ///< Indicates if the content is progressive
   bool      m_interlacedSourceFlag;                           ///< Indicates if the content is interlaced
   bool      m_chromaLocInfoPresentFlag;                       ///< Signals whether chroma_sample_loc_type_top_field and chroma_sample_loc_type_bottom_field are present
-  int       m_chromaSampleLocTypeTopField;                    ///< Specifies the location of chroma samples for top field
-  int       m_chromaSampleLocTypeBottomField;                 ///< Specifies the location of chroma samples for bottom field
-  int       m_chromaSampleLocType;                            ///< Specifies the location of chroma samples for progressive content
+  Chroma420LocType m_chromaSampleLocTypeTopField;      // Specifies the location of chroma samples for top field
+  Chroma420LocType m_chromaSampleLocTypeBottomField;   // Specifies the location of chroma samples for bottom field
+  Chroma420LocType m_chromaSampleLocType;   // Specifies the location of chroma samples for progressive content
   bool      m_overscanInfoPresentFlag;                        ///< Signals whether overscan_appropriate_flag is present
   bool      m_overscanAppropriateFlag;                        ///< Indicates whether conformant decoded pictures are suitable for display using overscan
   bool      m_videoFullRangeFlag;                             ///< Indicates the black level and range of luma and chroma signals
diff --git a/source/Lib/CommonLib/SequenceParameterSet.h b/source/Lib/CommonLib/SequenceParameterSet.h
index de81bd397e3825ab5c6b8ddc1341d582206f4853..983a2f3aee6aa4e5b01c74a524467793569dd4a8 100644
--- a/source/Lib/CommonLib/SequenceParameterSet.h
+++ b/source/Lib/CommonLib/SequenceParameterSet.h
@@ -57,10 +57,10 @@ private:
   int  m_transferCharacteristics;
   int  m_matrixCoefficients;
   bool m_videoFullRangeFlag;
-  bool m_chromaLocInfoPresentFlag;
-  int  m_chromaSampleLocTypeTopField;
-  int  m_chromaSampleLocTypeBottomField;
-  int  m_chromaSampleLocType;
+  bool             m_chromaLocInfoPresentFlag       = false;
+  Chroma420LocType m_chromaSampleLocTypeTopField    = Chroma420LocType::UNSPECIFIED;
+  Chroma420LocType m_chromaSampleLocTypeBottomField = Chroma420LocType::UNSPECIFIED;
+  Chroma420LocType m_chromaSampleLocType            = Chroma420LocType::UNSPECIFIED;
 
 public:
   VUI()
@@ -80,10 +80,6 @@ public:
     , m_transferCharacteristics(2)
     , m_matrixCoefficients(2)
     , m_videoFullRangeFlag(false)
-    , m_chromaLocInfoPresentFlag(false)
-    , m_chromaSampleLocTypeTopField(6)
-    , m_chromaSampleLocTypeBottomField(6)
-    , m_chromaSampleLocType(6)
   {}
 
   virtual ~VUI() {}
@@ -131,14 +127,14 @@ public:
   bool getChromaLocInfoPresentFlag() const       { return m_chromaLocInfoPresentFlag; }
   void setChromaLocInfoPresentFlag(bool i)       { m_chromaLocInfoPresentFlag = i; }
 
-  int  getChromaSampleLocTypeTopField() const    { return m_chromaSampleLocTypeTopField; }
-  void setChromaSampleLocTypeTopField(int i)     { m_chromaSampleLocTypeTopField = i; }
+  Chroma420LocType getChromaSampleLocTypeTopField() const { return m_chromaSampleLocTypeTopField; }
+  void             setChromaSampleLocTypeTopField(Chroma420LocType val) { m_chromaSampleLocTypeTopField = val; }
 
-  int  getChromaSampleLocTypeBottomField() const { return m_chromaSampleLocTypeBottomField; }
-  void setChromaSampleLocTypeBottomField(int i)  { m_chromaSampleLocTypeBottomField = i; }
+  Chroma420LocType getChromaSampleLocTypeBottomField() const { return m_chromaSampleLocTypeBottomField; }
+  void             setChromaSampleLocTypeBottomField(Chroma420LocType val) { m_chromaSampleLocTypeBottomField = val; }
 
-  int  getChromaSampleLocType() const            { return m_chromaSampleLocType; }
-  void setChromaSampleLocType(int i)             { m_chromaSampleLocType = i; }
+  Chroma420LocType getChromaSampleLocType() const { return m_chromaSampleLocType; }
+  void             setChromaSampleLocType(Chroma420LocType val) { m_chromaSampleLocType = val; }
 
   bool getOverscanInfoPresentFlag() const        { return m_overscanInfoPresentFlag; }
   void setOverscanInfoPresentFlag(bool i)        { m_overscanInfoPresentFlag = i; }
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index a1b83dd77a67e4cfc654881d62c575642076d2da..aedc31081cab304becf168c45ae5004f032157b6 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -427,6 +427,18 @@ enum class ChromaFormat : uint8_t
   UNDEFINED = NUM
 };
 
+enum class Chroma420LocType : uint8_t
+{
+  LEFT,
+  CENTER,
+  TOP_LEFT,
+  TOP,
+  BOTTOM_LEFT,
+  BOTTOM,
+  UNSPECIFIED,
+  NUM,
+};
+
 enum class ChannelType : uint8_t
 {
   LUMA   = 0,
diff --git a/source/Lib/DecoderLib/VLCReader.cpp b/source/Lib/DecoderLib/VLCReader.cpp
index db0f35edaac112de3c1d4d076718f5f985845208..64310f4764328dc3042016714546ab49a818c8ce 100644
--- a/source/Lib/DecoderLib/VLCReader.cpp
+++ b/source/Lib/DecoderLib/VLCReader.cpp
@@ -1156,12 +1156,19 @@ void  HLSyntaxReader::parseVUI(VUI* pcVUI, SPS *pcSPS)
   {
     if(pcVUI->getProgressiveSourceFlag() && !pcVUI->getInterlacedSourceFlag())
     {
-      xReadUvlc(   symbol, "vui_chroma_sample_loc_type" );        pcVUI->setChromaSampleLocType(symbol);
+      xReadUvlc(symbol, "vui_chroma_sample_loc_type");
+      CHECK(symbol >= to_underlying(Chroma420LocType::NUM), "vui_chroma_sample_loc_type out of range");
+      pcVUI->setChromaSampleLocType(static_cast<Chroma420LocType>(symbol));
     }
     else
     {
-      xReadUvlc(   symbol, "vui_chroma_sample_loc_type_top_field" );        pcVUI->setChromaSampleLocTypeTopField(symbol);
-      xReadUvlc(   symbol, "vui_chroma_sample_loc_type_bottom_field" );     pcVUI->setChromaSampleLocTypeBottomField(symbol);
+      xReadUvlc(symbol, "vui_chroma_sample_loc_type_top_field");
+      CHECK(symbol >= to_underlying(Chroma420LocType::NUM), "vui_chroma_sample_loc_type_top_field out of range");
+      pcVUI->setChromaSampleLocTypeTopField(static_cast<Chroma420LocType>(symbol));
+
+      xReadUvlc(symbol, "vui_chroma_sample_loc_type_bottom_field");
+      CHECK(symbol >= to_underlying(Chroma420LocType::NUM), "vui_chroma_sample_loc_type_bottom_field out of range");
+      pcVUI->setChromaSampleLocTypeBottomField(static_cast<Chroma420LocType>(symbol));
     }
   }
 
diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h
index de51148d91d731fcfd0d82e59026932f75b72ff3..50b32cbe8490863eda60cf4c9e42f6092bfff6de 100644
--- a/source/Lib/EncoderLib/EncCfg.h
+++ b/source/Lib/EncoderLib/EncCfg.h
@@ -950,9 +950,9 @@ protected:
   bool      m_progressiveSourceFlag;                          ///< Indicates if the content is progressive
   bool      m_interlacedSourceFlag;                           ///< Indicates if the content is interlaced
   bool      m_chromaLocInfoPresentFlag;                       ///< Signals whether chroma_sample_loc_type_top_field and chroma_sample_loc_type_bottom_field are present
-  int       m_chromaSampleLocTypeTopField;                    ///< Specifies the location of chroma samples for top field
-  int       m_chromaSampleLocTypeBottomField;                 ///< Specifies the location of chroma samples for bottom field
-  int       m_chromaSampleLocType;                            ///< Specifies the location of chroma samples for progressive content
+  Chroma420LocType m_chromaSampleLocTypeTopField;      // Specifies the location of chroma samples for top field
+  Chroma420LocType m_chromaSampleLocTypeBottomField;   // Specifies the location of chroma samples for bottom field
+  Chroma420LocType m_chromaSampleLocType;   // Specifies the location of chroma samples for progressive content
   bool      m_overscanInfoPresentFlag;                        ///< Signals whether overscan_appropriate_flag is present
   bool      m_overscanAppropriateFlag;                        ///< Indicates whether conformant decoded pictures are suitable for display using overscan
   bool      m_videoFullRangeFlag;                             ///< Indicates the black level and range of luma and chroma signals
@@ -2642,12 +2642,12 @@ public:
   void         setMatrixCoefficients(int i)                          { m_matrixCoefficients = i; }
   bool         getChromaLocInfoPresentFlag()                         { return m_chromaLocInfoPresentFlag; }
   void         setChromaLocInfoPresentFlag(bool i)                   { m_chromaLocInfoPresentFlag = i; }
-  int          getChromaSampleLocTypeTopField()                      { return m_chromaSampleLocTypeTopField; }
-  void         setChromaSampleLocTypeTopField(int i)                 { m_chromaSampleLocTypeTopField = i; }
-  int          getChromaSampleLocTypeBottomField()                   { return m_chromaSampleLocTypeBottomField; }
-  void         setChromaSampleLocTypeBottomField(int i)              { m_chromaSampleLocTypeBottomField = i; }
-  int          getChromaSampleLocType()                              { return m_chromaSampleLocType; }
-  void         setChromaSampleLocType(int i)                         { m_chromaSampleLocType = i; }
+  Chroma420LocType getChromaSampleLocTypeTopField() { return m_chromaSampleLocTypeTopField; }
+  void             setChromaSampleLocTypeTopField(Chroma420LocType val) { m_chromaSampleLocTypeTopField = val; }
+  Chroma420LocType getChromaSampleLocTypeBottomField() { return m_chromaSampleLocTypeBottomField; }
+  void             setChromaSampleLocTypeBottomField(Chroma420LocType val) { m_chromaSampleLocTypeBottomField = val; }
+  Chroma420LocType getChromaSampleLocType() { return m_chromaSampleLocType; }
+  void             setChromaSampleLocType(Chroma420LocType val) { m_chromaSampleLocType = val; }
   bool         getOverscanInfoPresentFlag()                          { return m_overscanInfoPresentFlag; }
   void         setOverscanInfoPresentFlag(bool i)                    { m_overscanInfoPresentFlag = i; }
   bool         getOverscanAppropriateFlag()                          { return m_overscanAppropriateFlag; }
diff --git a/source/Lib/EncoderLib/VLCWriter.cpp b/source/Lib/EncoderLib/VLCWriter.cpp
index 6369e8ba9b4073e86738029cd529262f7d063c28..aa14e4e15cba68ceb3209ce57dcf468491c0876c 100644
--- a/source/Lib/EncoderLib/VLCWriter.cpp
+++ b/source/Lib/EncoderLib/VLCWriter.cpp
@@ -728,12 +728,12 @@ void HLSWriter::codeVUI( const VUI *pcVUI, const SPS* pcSPS )
   {
     if(pcVUI->getProgressiveSourceFlag() && !pcVUI->getInterlacedSourceFlag())
     {
-      xWriteUvlc(pcVUI->getChromaSampleLocType(),         "vui_chroma_sample_loc_type");
+      xWriteUvlc(to_underlying(pcVUI->getChromaSampleLocType()), "vui_chroma_sample_loc_type");
     }
     else
     {
-      xWriteUvlc(pcVUI->getChromaSampleLocTypeTopField(),         "vui_chroma_sample_loc_type_top_field");
-      xWriteUvlc(pcVUI->getChromaSampleLocTypeBottomField(),      "vui_chroma_sample_loc_type_bottom_field");
+      xWriteUvlc(to_underlying(pcVUI->getChromaSampleLocTypeTopField()), "vui_chroma_sample_loc_type_top_field");
+      xWriteUvlc(to_underlying(pcVUI->getChromaSampleLocTypeBottomField()), "vui_chroma_sample_loc_type_bottom_field");
     }
   }
   if(!isByteAligned())
diff --git a/source/Lib/Utilities/VideoIOYuv.cpp b/source/Lib/Utilities/VideoIOYuv.cpp
index 98123631d2b52a47e5c0f9a694102d4b8895d5a6..d7618f378290ab65919824020e350454a98abffb 100644
--- a/source/Lib/Utilities/VideoIOYuv.cpp
+++ b/source/Lib/Utilities/VideoIOYuv.cpp
@@ -176,7 +176,9 @@ void VideoIOYuv::open(const std::string &fileName, bool bWriteMode, const BitDep
         Fraction     dummyFrameRate;
         int          dummyBitDepth     = 0;
         ChromaFormat dummyChromaFormat = ChromaFormat::_420;
-        parseY4mFileHeader(fileName, dummyWidth, dummyHeight, dummyFrameRate, dummyBitDepth, dummyChromaFormat);
+        Chroma420LocType dummyLocType      = Chroma420LocType::UNSPECIFIED;
+        parseY4mFileHeader(fileName, dummyWidth, dummyHeight, dummyFrameRate, dummyBitDepth, dummyChromaFormat,
+                           dummyLocType);
       }
     }
     m_cHandle.open(fileName.c_str(), std::ios::binary | std::ios::in);
@@ -195,8 +197,42 @@ void VideoIOYuv::open(const std::string &fileName, bool bWriteMode, const BitDep
   return;
 }
 
+struct Y4mChromaFormat
+{
+  char             name[16];
+  int              bitDepth;
+  ChromaFormat     chromaFormat;
+  Chroma420LocType locType;
+};
+
+static const std::array y4mChromaFormats = {
+  Y4mChromaFormat{ "mono9", 9, ChromaFormat::_400, Chroma420LocType::UNSPECIFIED },
+  Y4mChromaFormat{ "mono10", 10, ChromaFormat::_400, Chroma420LocType::UNSPECIFIED },
+  Y4mChromaFormat{ "mono12", 12, ChromaFormat::_400, Chroma420LocType::UNSPECIFIED },
+  Y4mChromaFormat{ "mono", 8, ChromaFormat::_400, Chroma420LocType::UNSPECIFIED },
+
+  Y4mChromaFormat{ "420jpeg", 8, ChromaFormat::_420, Chroma420LocType::CENTER },
+  Y4mChromaFormat{ "420mpeg2", 8, ChromaFormat::_420, Chroma420LocType::LEFT },
+  Y4mChromaFormat{ "420paldv", 8, ChromaFormat::_420, Chroma420LocType::TOP_LEFT },
+
+  Y4mChromaFormat{ "420p9", 9, ChromaFormat::_420, Chroma420LocType::UNSPECIFIED },
+  Y4mChromaFormat{ "420p10", 10, ChromaFormat::_420, Chroma420LocType::UNSPECIFIED },
+  Y4mChromaFormat{ "420p12", 12, ChromaFormat::_420, Chroma420LocType::UNSPECIFIED },
+  Y4mChromaFormat{ "420", 8, ChromaFormat::_420, Chroma420LocType::UNSPECIFIED },
+
+  Y4mChromaFormat{ "422p9", 9, ChromaFormat::_422, Chroma420LocType::UNSPECIFIED },
+  Y4mChromaFormat{ "422p10", 10, ChromaFormat::_422, Chroma420LocType::UNSPECIFIED },
+  Y4mChromaFormat{ "422p12", 12, ChromaFormat::_422, Chroma420LocType::UNSPECIFIED },
+  Y4mChromaFormat{ "422", 8, ChromaFormat::_422, Chroma420LocType::UNSPECIFIED },
+
+  Y4mChromaFormat{ "444p9", 9, ChromaFormat::_444, Chroma420LocType::UNSPECIFIED },
+  Y4mChromaFormat{ "444p10", 10, ChromaFormat::_444, Chroma420LocType::UNSPECIFIED },
+  Y4mChromaFormat{ "444p12", 12, ChromaFormat::_444, Chroma420LocType::UNSPECIFIED },
+  Y4mChromaFormat{ "444", 8, ChromaFormat::_444, Chroma420LocType::UNSPECIFIED },
+};
+
 void VideoIOYuv::parseY4mFileHeader(const std::string& fileName, int& width, int& height, Fraction& frameRate,
-                                    int& bitDepth, ChromaFormat& chromaFormat)
+                                    int& bitDepth, ChromaFormat& chromaFormat, Chroma420LocType& locType)
 {
   m_cHandle.open(fileName.c_str(), std::ios::binary | std::ios::in);
   CHECK(m_cHandle.fail(), "File open failed.")
@@ -218,45 +254,22 @@ void VideoIOYuv::parseY4mFileHeader(const std::string& fileName, int& width, int
   // parse Y4M header info
   for (int i = Y4M_SIGNATURE_LENGTH; i < m_inY4mFileHeaderLength; i++)
   {
-    int numerator = 0, denominator = 0, pos = 0;
+    int numerator = 0, denominator = 0;
     switch (header[i])
     {
     case 'W': sscanf(header + i + 1, "%d", &width); break;
     case 'H': sscanf(header + i + 1, "%d", &height); break;
     case 'C':
-      if (strncmp(&header[i + 1], "mono", 4) == 0)
+      for (const auto& cf: y4mChromaFormats)
       {
-        chromaFormat = ChromaFormat::_400;
-        pos          = i + 5;
-      }
-      else if (strncmp(&header[i + 1], "420", 3) == 0)
-      {
-        chromaFormat = ChromaFormat::_420;
-        pos          = i + 4;
-        if (strncmp(&header[pos], "jpeg", 4) == 0)
-        {
-          pos += 4;
-        }
-        else if (strncmp(&header[pos], "paldv", 5) == 0)
+        if (strncmp(&header[i + 1], cf.name, strlen(cf.name)) == 0)
         {
-          pos += 5;
+          chromaFormat = cf.chromaFormat;
+          locType      = cf.locType;
+          bitDepth     = cf.bitDepth;
+          break;
         }
       }
-      else if (strncmp(&header[i + 1], "422", 3) == 0)
-      {
-        chromaFormat = ChromaFormat::_422;
-        pos          = i + 4;
-      }
-      else if (strncmp(&header[i + 1], "444", 3) == 0)
-      {
-        chromaFormat = ChromaFormat::_444;
-        pos          = i + 4;
-      }
-      bitDepth = 8;
-      if (header[pos] == 'p')
-      {
-        sscanf(&header[pos + 1], "%d", &bitDepth);
-      }
       break;
     case 'F':
       if (sscanf(header + i + 1, "%d:%d", &numerator, &denominator) == 2)
@@ -281,13 +294,14 @@ void VideoIOYuv::parseY4mFileHeader(const std::string& fileName, int& width, int
 }
 
 void VideoIOYuv::setOutputY4mInfo(int width, int height, const Fraction& frameRate, int bitDepth,
-                                  ChromaFormat chromaFormat)
+                                  ChromaFormat chromaFormat, Chroma420LocType locType)
 {
   m_outPicWidth     = width;
   m_outPicHeight    = height;
   m_outBitDepth     = bitDepth;
   m_outFrameRate    = frameRate;
   m_outChromaFormat = chromaFormat;
+  m_outLocType      = locType;
 }
 
 void VideoIOYuv::writeY4mFileHeader()
@@ -299,26 +313,32 @@ void VideoIOYuv::writeY4mFileHeader()
   header += "H" + std::to_string(m_outPicHeight) + " ";
   header += "F" + std::to_string(m_outFrameRate.num) + ":" + std::to_string(m_outFrameRate.den) + " ";
   header += "Ip A0:0 ";
-  switch (m_outChromaFormat)
+  header += "C";
+  bool found = false;
+  for (const auto& cf: y4mChromaFormats)
   {
-  case ChromaFormat::_400:
-    header += "Cmono";
-    break;
-  case ChromaFormat::_420:
-    header += "C420";
-    break;
-  case ChromaFormat::_422:
-    header += "C422";
-    break;
-  case ChromaFormat::_444:
-    header += "C444";
-    break;
-  default: CHECK(true, "Unknow chroma format");
+    if (m_outBitDepth == cf.bitDepth && m_outChromaFormat == cf.chromaFormat && m_outLocType == cf.locType)
+    {
+      header += cf.name;
+      found = true;
+      break;
+    }
   }
-  if (m_outBitDepth > 8)
+  if (!found)
   {
-    header += "p" + std::to_string(m_outBitDepth);
+    for (const auto& cf: y4mChromaFormats)
+    {
+      if (m_outBitDepth == cf.bitDepth && m_outChromaFormat == cf.chromaFormat
+          && Chroma420LocType::UNSPECIFIED == cf.locType)
+      {
+        header += cf.name;
+        found = true;
+        msg(WARNING, "Value for chroma sample location unsupported by y4m. Signalling unspecified location.");
+        break;
+      }
+    }
   }
+  CHECK(!found, "Format unsupported by y4m");
   header += "\n";
   // not write extension/comment
 
diff --git a/source/Lib/Utilities/VideoIOYuv.h b/source/Lib/Utilities/VideoIOYuv.h
index fc006aeb3d078696604cbed0c6b0b29b3259d644..e353c48241878bbf08e377887dbb4aa9f2eab73d 100644
--- a/source/Lib/Utilities/VideoIOYuv.h
+++ b/source/Lib/Utilities/VideoIOYuv.h
@@ -67,6 +67,7 @@ private:
   int          m_outBitDepth           = 0;
   Fraction     m_outFrameRate;
   ChromaFormat m_outChromaFormat       = ChromaFormat::_420;
+  Chroma420LocType m_outLocType            = Chroma420LocType::UNSPECIFIED;
   bool         m_outY4m                = false;
 
 public:
@@ -74,8 +75,9 @@ public:
   virtual ~VideoIOYuv()  {}
 
   void parseY4mFileHeader(const std::string& fileName, int& width, int& height, Fraction& frameRate, int& bitDepth,
-                          ChromaFormat& chromaFormat);
-  void setOutputY4mInfo(int width, int height, const Fraction& frameRate, int bitDepth, ChromaFormat chromaFormat);
+                          ChromaFormat& chromaFormat, Chroma420LocType& locType);
+  void setOutputY4mInfo(int width, int height, const Fraction& frameRate, int bitDepth, ChromaFormat chromaFormat,
+                        Chroma420LocType locType);
   void writeY4mFileHeader();
   void open(const std::string &fileName, bool bWriteMode, const BitDepths &fileBitDepth,
             const BitDepths &MSBExtendedBitDepth,