Add chroma siting to ColorSpace

Bug: webrtc:8651
Change-Id: I82263e8b6cdcc3ebf699f5e3ebbde04e46982efb
Reviewed-on: https://webrtc-review.googlesource.com/c/113424
Reviewed-by: Niels Moller <nisse@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Commit-Queue: Johannes Kron <kron@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25944}
diff --git a/api/video/color_space.cc b/api/video/color_space.cc
index c6d9259..710bb43 100644
--- a/api/video/color_space.cc
+++ b/api/video/color_space.cc
@@ -10,6 +10,7 @@
 
 #include "api/video/color_space.h"
 
+namespace webrtc {
 namespace {
 // Try to convert |enum_value| into the enum class T. |enum_bitmask| is created
 // by the funciton below. Returns true if conversion was successful, false
@@ -50,9 +51,17 @@
   return MakeMask(0, N, values);
 }
 
-}  // namespace
+bool SetChromaSitingFromUint8(uint8_t enum_value,
+                              ColorSpace::ChromaSiting* chroma_siting) {
+  constexpr ColorSpace::ChromaSiting kChromaSitings[] = {
+      ColorSpace::ChromaSiting::kUnspecified,
+      ColorSpace::ChromaSiting::kCollocated, ColorSpace::ChromaSiting::kHalf};
+  constexpr uint64_t enum_bitmask = CreateEnumBitmask(kChromaSitings);
 
-namespace webrtc {
+  return SetFromUint8(enum_value, enum_bitmask, chroma_siting);
+}
+
+}  // namespace
 
 ColorSpace::ColorSpace() = default;
 ColorSpace::ColorSpace(const ColorSpace& other) = default;
@@ -63,17 +72,27 @@
                        TransferID transfer,
                        MatrixID matrix,
                        RangeID range)
-    : ColorSpace(primaries, transfer, matrix, range, nullptr) {}
+    : ColorSpace(primaries,
+                 transfer,
+                 matrix,
+                 range,
+                 ChromaSiting::kUnspecified,
+                 ChromaSiting::kUnspecified,
+                 nullptr) {}
 
 ColorSpace::ColorSpace(PrimaryID primaries,
                        TransferID transfer,
                        MatrixID matrix,
                        RangeID range,
+                       ChromaSiting chroma_siting_horz,
+                       ChromaSiting chroma_siting_vert,
                        const HdrMetadata* hdr_metadata)
     : primaries_(primaries),
       transfer_(transfer),
       matrix_(matrix),
       range_(range),
+      chroma_siting_horizontal_(chroma_siting_horz),
+      chroma_siting_vertical_(chroma_siting_vert),
       hdr_metadata_(hdr_metadata ? absl::make_optional(*hdr_metadata)
                                  : absl::nullopt) {}
 
@@ -93,13 +112,21 @@
   return range_;
 }
 
+ColorSpace::ChromaSiting ColorSpace::chroma_siting_horizontal() const {
+  return chroma_siting_horizontal_;
+}
+
+ColorSpace::ChromaSiting ColorSpace::chroma_siting_vertical() const {
+  return chroma_siting_vertical_;
+}
+
 const HdrMetadata* ColorSpace::hdr_metadata() const {
   return hdr_metadata_ ? &*hdr_metadata_ : nullptr;
 }
 
 bool ColorSpace::set_primaries_from_uint8(uint8_t enum_value) {
   constexpr PrimaryID kPrimaryIds[] = {
-      PrimaryID::kBT709,      PrimaryID::kUNSPECIFIED, PrimaryID::kBT470M,
+      PrimaryID::kBT709,      PrimaryID::kUnspecified, PrimaryID::kBT470M,
       PrimaryID::kBT470BG,    PrimaryID::kSMPTE170M,   PrimaryID::kSMPTE240M,
       PrimaryID::kFILM,       PrimaryID::kBT2020,      PrimaryID::kSMPTEST428,
       PrimaryID::kSMPTEST431, PrimaryID::kSMPTEST432,  PrimaryID::kJEDECP22};
@@ -110,7 +137,7 @@
 
 bool ColorSpace::set_transfer_from_uint8(uint8_t enum_value) {
   constexpr TransferID kTransferIds[] = {
-      TransferID::kBT709,       TransferID::kUNSPECIFIED,
+      TransferID::kBT709,       TransferID::kUnspecified,
       TransferID::kGAMMA22,     TransferID::kGAMMA28,
       TransferID::kSMPTE170M,   TransferID::kSMPTE240M,
       TransferID::kLINEAR,      TransferID::kLOG,
@@ -126,7 +153,7 @@
 
 bool ColorSpace::set_matrix_from_uint8(uint8_t enum_value) {
   constexpr MatrixID kMatrixIds[] = {
-      MatrixID::kRGB,       MatrixID::kBT709,       MatrixID::kUNSPECIFIED,
+      MatrixID::kRGB,       MatrixID::kBT709,       MatrixID::kUnspecified,
       MatrixID::kFCC,       MatrixID::kBT470BG,     MatrixID::kSMPTE170M,
       MatrixID::kSMPTE240M, MatrixID::kYCOCG,       MatrixID::kBT2020_NCL,
       MatrixID::kBT2020_CL, MatrixID::kSMPTE2085,   MatrixID::kCDNCLS,
@@ -144,6 +171,14 @@
   return SetFromUint8(enum_value, enum_bitmask, &range_);
 }
 
+bool ColorSpace::set_chroma_siting_horizontal_from_uint8(uint8_t enum_value) {
+  return SetChromaSitingFromUint8(enum_value, &chroma_siting_horizontal_);
+}
+
+bool ColorSpace::set_chroma_siting_vertical_from_uint8(uint8_t enum_value) {
+  return SetChromaSitingFromUint8(enum_value, &chroma_siting_vertical_);
+}
+
 void ColorSpace::set_hdr_metadata(const HdrMetadata* hdr_metadata) {
   hdr_metadata_ =
       hdr_metadata ? absl::make_optional(*hdr_metadata) : absl::nullopt;
diff --git a/api/video/color_space.h b/api/video/color_space.h
index 58a04eb..91b4e17 100644
--- a/api/video/color_space.h
+++ b/api/video/color_space.h
@@ -36,7 +36,7 @@
   enum class PrimaryID : uint8_t {
     // The indices are equal to the values specified in T-REC H.273 Table 2.
     kBT709 = 1,
-    kUNSPECIFIED = 2,
+    kUnspecified = 2,
     kBT470M = 4,
     kBT470BG = 5,
     kSMPTE170M = 6,  // Identical to BT601
@@ -54,7 +54,7 @@
   enum class TransferID : uint8_t {
     // The indices are equal to the values specified in T-REC H.273 Table 3.
     kBT709 = 1,
-    kUNSPECIFIED = 2,
+    kUnspecified = 2,
     kGAMMA22 = 4,
     kGAMMA28 = 5,
     kSMPTE170M = 6,
@@ -78,7 +78,7 @@
     // The indices are equal to the values specified in T-REC H.273 Table 4.
     kRGB = 0,
     kBT709 = 1,
-    kUNSPECIFIED = 2,
+    kUnspecified = 2,
     kFCC = 4,
     kBT470BG = 5,
     kSMPTE170M = 6,
@@ -108,6 +108,19 @@
     // corresponding change to kRangeIds.
   };
 
+  enum class ChromaSiting {
+    // Chroma siting specifies how chroma is subsampled relative to the luma
+    // samples in a YUV video frame.
+    // The indices are equal to the values specified at
+    // https://www.webmproject.org/docs/container/#colour for the element
+    // ChromaSitingVert and ChromaSitingHorz.
+    kUnspecified = 0,
+    kCollocated = 1,
+    kHalf = 2,
+    // When adding/removing entries here, please make sure to do the
+    // corresponding change to kChromaSitings.
+  };
+
   ColorSpace();
   ColorSpace(const ColorSpace& other);
   ColorSpace(ColorSpace&& other);
@@ -115,15 +128,19 @@
   ColorSpace(PrimaryID primaries,
              TransferID transfer,
              MatrixID matrix,
-             RangeID full_range);
+             RangeID range);
   ColorSpace(PrimaryID primaries,
              TransferID transfer,
              MatrixID matrix,
              RangeID range,
+             ChromaSiting chroma_siting_horizontal,
+             ChromaSiting chroma_siting_vertical,
              const HdrMetadata* hdr_metadata);
   friend bool operator==(const ColorSpace& lhs, const ColorSpace& rhs) {
     return lhs.primaries_ == rhs.primaries_ && lhs.transfer_ == rhs.transfer_ &&
            lhs.matrix_ == rhs.matrix_ && lhs.range_ == rhs.range_ &&
+           lhs.chroma_siting_horizontal_ == rhs.chroma_siting_horizontal_ &&
+           lhs.chroma_siting_vertical_ == rhs.chroma_siting_vertical_ &&
            lhs.hdr_metadata_ == rhs.hdr_metadata_;
   }
   friend bool operator!=(const ColorSpace& lhs, const ColorSpace& rhs) {
@@ -134,22 +151,27 @@
   TransferID transfer() const;
   MatrixID matrix() const;
   RangeID range() const;
+  ChromaSiting chroma_siting_horizontal() const;
+  ChromaSiting chroma_siting_vertical() const;
   const HdrMetadata* hdr_metadata() const;
 
   bool set_primaries_from_uint8(uint8_t enum_value);
   bool set_transfer_from_uint8(uint8_t enum_value);
   bool set_matrix_from_uint8(uint8_t enum_value);
   bool set_range_from_uint8(uint8_t enum_value);
+  bool set_chroma_siting_horizontal_from_uint8(uint8_t enum_value);
+  bool set_chroma_siting_vertical_from_uint8(uint8_t enum_value);
   void set_hdr_metadata(const HdrMetadata* hdr_metadata);
 
  private:
-  PrimaryID primaries_ = PrimaryID::kUNSPECIFIED;
-  TransferID transfer_ = TransferID::kUNSPECIFIED;
-  MatrixID matrix_ = MatrixID::kUNSPECIFIED;
+  PrimaryID primaries_ = PrimaryID::kUnspecified;
+  TransferID transfer_ = TransferID::kUnspecified;
+  MatrixID matrix_ = MatrixID::kUnspecified;
   RangeID range_ = RangeID::kInvalid;
+  ChromaSiting chroma_siting_horizontal_ = ChromaSiting::kUnspecified;
+  ChromaSiting chroma_siting_vertical_ = ChromaSiting::kUnspecified;
   absl::optional<HdrMetadata> hdr_metadata_;
 };
 
 }  // namespace webrtc
-
 #endif  // API_VIDEO_COLOR_SPACE_H_
diff --git a/api/video/test/color_space_unittest.cc b/api/video/test/color_space_unittest.cc
index b71bcef..e1318ce 100644
--- a/api/video/test/color_space_unittest.cc
+++ b/api/video/test/color_space_unittest.cc
@@ -52,4 +52,22 @@
   EXPECT_FALSE(color_space.set_range_from_uint8(4));
 }
 
+TEST(ColorSpace, TestSettingChromaSitingHorizontalFromUint8) {
+  ColorSpace color_space;
+  EXPECT_TRUE(color_space.set_chroma_siting_horizontal_from_uint8(
+      static_cast<uint8_t>(ColorSpace::ChromaSiting::kCollocated)));
+  EXPECT_EQ(ColorSpace::ChromaSiting::kCollocated,
+            color_space.chroma_siting_horizontal());
+  EXPECT_FALSE(color_space.set_chroma_siting_horizontal_from_uint8(3));
+}
+
+TEST(ColorSpace, TestSettingChromaSitingVerticalFromUint8) {
+  ColorSpace color_space;
+  EXPECT_TRUE(color_space.set_chroma_siting_vertical_from_uint8(
+      static_cast<uint8_t>(ColorSpace::ChromaSiting::kHalf)));
+  EXPECT_EQ(ColorSpace::ChromaSiting::kHalf,
+            color_space.chroma_siting_vertical());
+  EXPECT_FALSE(color_space.set_chroma_siting_vertical_from_uint8(3));
+}
+
 }  // namespace webrtc
diff --git a/modules/video_coding/codecs/h264/h264_color_space.cc b/modules/video_coding/codecs/h264/h264_color_space.cc
index b764d4e..2454c20 100644
--- a/modules/video_coding/codecs/h264/h264_color_space.cc
+++ b/modules/video_coding/codecs/h264/h264_color_space.cc
@@ -13,7 +13,7 @@
 namespace webrtc {
 
 ColorSpace ExtractH264ColorSpace(AVCodecContext* codec) {
-  ColorSpace::PrimaryID primaries = ColorSpace::PrimaryID::kUNSPECIFIED;
+  ColorSpace::PrimaryID primaries = ColorSpace::PrimaryID::kUnspecified;
   switch (codec->color_primaries) {
     case AVCOL_PRI_BT709:
       primaries = ColorSpace::PrimaryID::kBT709;
@@ -55,7 +55,7 @@
       break;
   }
 
-  ColorSpace::TransferID transfer = ColorSpace::TransferID::kUNSPECIFIED;
+  ColorSpace::TransferID transfer = ColorSpace::TransferID::kUnspecified;
   switch (codec->color_trc) {
     case AVCOL_TRC_BT709:
       transfer = ColorSpace::TransferID::kBT709;
@@ -112,7 +112,7 @@
       break;
   }
 
-  ColorSpace::MatrixID matrix = ColorSpace::MatrixID::kUNSPECIFIED;
+  ColorSpace::MatrixID matrix = ColorSpace::MatrixID::kUnspecified;
   switch (codec->colorspace) {
     case AVCOL_SPC_RGB:
       matrix = ColorSpace::MatrixID::kRGB;
diff --git a/modules/video_coding/codecs/h264/test/h264_impl_unittest.cc b/modules/video_coding/codecs/h264/test/h264_impl_unittest.cc
index 09c8c22..70326d3 100644
--- a/modules/video_coding/codecs/h264/test/h264_impl_unittest.cc
+++ b/modules/video_coding/codecs/h264/test/h264_impl_unittest.cc
@@ -72,10 +72,14 @@
   EXPECT_GT(I420PSNR(input_frame, decoded_frame.get()), 36);
 
   const ColorSpace color_space = *decoded_frame->color_space();
-  EXPECT_EQ(ColorSpace::PrimaryID::kUNSPECIFIED, color_space.primaries());
-  EXPECT_EQ(ColorSpace::TransferID::kUNSPECIFIED, color_space.transfer());
-  EXPECT_EQ(ColorSpace::MatrixID::kUNSPECIFIED, color_space.matrix());
+  EXPECT_EQ(ColorSpace::PrimaryID::kUnspecified, color_space.primaries());
+  EXPECT_EQ(ColorSpace::TransferID::kUnspecified, color_space.transfer());
+  EXPECT_EQ(ColorSpace::MatrixID::kUnspecified, color_space.matrix());
   EXPECT_EQ(ColorSpace::RangeID::kLimited, color_space.range());
+  EXPECT_EQ(ColorSpace::ChromaSiting::kUnspecified,
+            color_space.chroma_siting_horizontal());
+  EXPECT_EQ(ColorSpace::ChromaSiting::kUnspecified,
+            color_space.chroma_siting_vertical());
 }
 
 TEST_F(TestH264Impl, MAYBE_DecodedQpEqualsEncodedQp) {
diff --git a/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc b/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc
index e081d76..5386a8e 100644
--- a/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc
+++ b/modules/video_coding/codecs/vp9/test/vp9_impl_unittest.cc
@@ -126,10 +126,11 @@
 
   ColorSpace CreateTestColorSpace() const {
     HdrMetadata hdr_metadata = CreateTestHdrMetadata();
-    ColorSpace color_space(ColorSpace::PrimaryID::kBT709,
-                           ColorSpace::TransferID::kGAMMA22,
-                           ColorSpace::MatrixID::kSMPTE2085,
-                           ColorSpace::RangeID::kFull, &hdr_metadata);
+    ColorSpace color_space(
+        ColorSpace::PrimaryID::kBT709, ColorSpace::TransferID::kGAMMA22,
+        ColorSpace::MatrixID::kSMPTE2085, ColorSpace::RangeID::kFull,
+        ColorSpace::ChromaSiting::kCollocated,
+        ColorSpace::ChromaSiting::kCollocated, &hdr_metadata);
     return color_space;
   }
 };
@@ -157,10 +158,14 @@
   EXPECT_GT(I420PSNR(input_frame, decoded_frame.get()), 36);
 
   const ColorSpace color_space = *decoded_frame->color_space();
-  EXPECT_EQ(ColorSpace::PrimaryID::kUNSPECIFIED, color_space.primaries());
-  EXPECT_EQ(ColorSpace::TransferID::kUNSPECIFIED, color_space.transfer());
-  EXPECT_EQ(ColorSpace::MatrixID::kUNSPECIFIED, color_space.matrix());
+  EXPECT_EQ(ColorSpace::PrimaryID::kUnspecified, color_space.primaries());
+  EXPECT_EQ(ColorSpace::TransferID::kUnspecified, color_space.transfer());
+  EXPECT_EQ(ColorSpace::MatrixID::kUnspecified, color_space.matrix());
   EXPECT_EQ(ColorSpace::RangeID::kLimited, color_space.range());
+  EXPECT_EQ(ColorSpace::ChromaSiting::kUnspecified,
+            color_space.chroma_siting_horizontal());
+  EXPECT_EQ(ColorSpace::ChromaSiting::kUnspecified,
+            color_space.chroma_siting_vertical());
 }
 
 // We only test the encoder here, since the decoded frame rotation is set based
diff --git a/modules/video_coding/codecs/vp9/vp9_impl.cc b/modules/video_coding/codecs/vp9/vp9_impl.cc
index 3fc0cd8..c87c9bb 100644
--- a/modules/video_coding/codecs/vp9/vp9_impl.cc
+++ b/modules/video_coding/codecs/vp9/vp9_impl.cc
@@ -70,9 +70,9 @@
 ColorSpace ExtractVP9ColorSpace(vpx_color_space_t space_t,
                                 vpx_color_range_t range_t,
                                 unsigned int bit_depth) {
-  ColorSpace::PrimaryID primaries = ColorSpace::PrimaryID::kUNSPECIFIED;
-  ColorSpace::TransferID transfer = ColorSpace::TransferID::kUNSPECIFIED;
-  ColorSpace::MatrixID matrix = ColorSpace::MatrixID::kUNSPECIFIED;
+  ColorSpace::PrimaryID primaries = ColorSpace::PrimaryID::kUnspecified;
+  ColorSpace::TransferID transfer = ColorSpace::TransferID::kUnspecified;
+  ColorSpace::MatrixID matrix = ColorSpace::MatrixID::kUnspecified;
   switch (space_t) {
     case VPX_CS_BT_601:
     case VPX_CS_SMPTE_170: