Move h264_profile_level_id and vp9_profile to api/video_codecs

This is a refactor to simplify a follow-up CL of adding
SdpVideoFormat::IsSameCodec.

The original files media/base/h264_profile_level_id.* and
media/base/vp9_profile.h must be kept until downstream projects
stop using them.

Bug: chroimium:1187565
Change-Id: Ib39eca095a3d61939a914d9bffaf4b891ddd222f
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/215236
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Kári Helgason <kthelgason@webrtc.org>
Reviewed-by: Niels Moller <nisse@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Commit-Queue: Johannes Kron <kron@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#33782}
diff --git a/api/test/DEPS b/api/test/DEPS
index d97ac49..2784fcb 100644
--- a/api/test/DEPS
+++ b/api/test/DEPS
@@ -35,7 +35,4 @@
   "create_frame_generator\.h": [
     "+system_wrappers/include/clock.h",
   ],
-  "videocodec_test_fixture\.h": [
-    "+media/base/h264_profile_level_id.h"
-  ],
 }
diff --git a/api/test/videocodec_test_fixture.h b/api/test/videocodec_test_fixture.h
index 379d46d..e0f804f 100644
--- a/api/test/videocodec_test_fixture.h
+++ b/api/test/videocodec_test_fixture.h
@@ -59,7 +59,7 @@
   class EncodedFrameChecker {
    public:
     virtual ~EncodedFrameChecker() = default;
-    virtual void CheckEncodedFrame(webrtc::VideoCodecType codec,
+    virtual void CheckEncodedFrame(VideoCodecType codec,
                                    const EncodedImage& encoded_frame) const = 0;
   };
 
@@ -123,16 +123,16 @@
     bool encode_in_real_time = false;
 
     // Codec settings to use.
-    webrtc::VideoCodec codec_settings;
+    VideoCodec codec_settings;
 
     // Name of the codec being tested.
     std::string codec_name;
 
     // H.264 specific settings.
     struct H264CodecSettings {
-      H264::Profile profile = H264::kProfileConstrainedBaseline;
+      H264Profile profile = H264Profile::kProfileConstrainedBaseline;
       H264PacketizationMode packetization_mode =
-          webrtc::H264PacketizationMode::NonInterleaved;
+          H264PacketizationMode::NonInterleaved;
     } h264_codec_settings;
 
     // Custom checker that will be called for each frame.
diff --git a/api/video_codecs/BUILD.gn b/api/video_codecs/BUILD.gn
index a990276..4e28cd7 100644
--- a/api/video_codecs/BUILD.gn
+++ b/api/video_codecs/BUILD.gn
@@ -15,6 +15,8 @@
 rtc_library("video_codecs_api") {
   visibility = [ "*" ]
   sources = [
+    "h264_profile_level_id.cc",
+    "h264_profile_level_id.h",
     "sdp_video_format.cc",
     "sdp_video_format.h",
     "spatial_layer.cc",
@@ -35,6 +37,8 @@
     "vp8_frame_config.h",
     "vp8_temporal_layers.cc",
     "vp8_temporal_layers.h",
+    "vp9_profile.cc",
+    "vp9_profile.h",
   ]
 
   deps = [
@@ -138,7 +142,6 @@
     ":video_codecs_api",
     "..:fec_controller_api",
     "../../api/video:video_frame",
-    "../../media:rtc_h264_profile_id",
     "../../media:rtc_media_base",
     "../../modules/video_coding:video_codec_interface",
     "../../modules/video_coding:video_coding_utility",
diff --git a/api/video_codecs/h264_profile_level_id.cc b/api/video_codecs/h264_profile_level_id.cc
new file mode 100644
index 0000000..fa47758
--- /dev/null
+++ b/api/video_codecs/h264_profile_level_id.cc
@@ -0,0 +1,252 @@
+/*
+ *  Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "api/video_codecs/h264_profile_level_id.h"
+
+#include <cstdio>
+#include <cstdlib>
+#include <string>
+
+#include "rtc_base/arraysize.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+namespace {
+
+const char kProfileLevelId[] = "profile-level-id";
+
+// For level_idc=11 and profile_idc=0x42, 0x4D, or 0x58, the constraint set3
+// flag specifies if level 1b or level 1.1 is used.
+const uint8_t kConstraintSet3Flag = 0x10;
+
+// Convert a string of 8 characters into a byte where the positions containing
+// character c will have their bit set. For example, c = 'x', str = "x1xx0000"
+// will return 0b10110000. constexpr is used so that the pattern table in
+// kProfilePatterns is statically initialized.
+constexpr uint8_t ByteMaskString(char c, const char (&str)[9]) {
+  return (str[0] == c) << 7 | (str[1] == c) << 6 | (str[2] == c) << 5 |
+         (str[3] == c) << 4 | (str[4] == c) << 3 | (str[5] == c) << 2 |
+         (str[6] == c) << 1 | (str[7] == c) << 0;
+}
+
+// Class for matching bit patterns such as "x1xx0000" where 'x' is allowed to be
+// either 0 or 1.
+class BitPattern {
+ public:
+  explicit constexpr BitPattern(const char (&str)[9])
+      : mask_(~ByteMaskString('x', str)),
+        masked_value_(ByteMaskString('1', str)) {}
+
+  bool IsMatch(uint8_t value) const { return masked_value_ == (value & mask_); }
+
+ private:
+  const uint8_t mask_;
+  const uint8_t masked_value_;
+};
+
+// Table for converting between profile_idc/profile_iop to H264Profile.
+struct ProfilePattern {
+  const uint8_t profile_idc;
+  const BitPattern profile_iop;
+  const H264Profile profile;
+};
+
+// This is from https://tools.ietf.org/html/rfc6184#section-8.1.
+constexpr ProfilePattern kProfilePatterns[] = {
+    {0x42, BitPattern("x1xx0000"), H264Profile::kProfileConstrainedBaseline},
+    {0x4D, BitPattern("1xxx0000"), H264Profile::kProfileConstrainedBaseline},
+    {0x58, BitPattern("11xx0000"), H264Profile::kProfileConstrainedBaseline},
+    {0x42, BitPattern("x0xx0000"), H264Profile::kProfileBaseline},
+    {0x58, BitPattern("10xx0000"), H264Profile::kProfileBaseline},
+    {0x4D, BitPattern("0x0x0000"), H264Profile::kProfileMain},
+    {0x64, BitPattern("00000000"), H264Profile::kProfileHigh},
+    {0x64, BitPattern("00001100"), H264Profile::kProfileConstrainedHigh}};
+
+struct LevelConstraint {
+  const int max_macroblocks_per_second;
+  const int max_macroblock_frame_size;
+  const H264Level level;
+};
+
+// This is from ITU-T H.264 (02/2016) Table A-1 – Level limits.
+static constexpr LevelConstraint kLevelConstraints[] = {
+    {1485, 99, H264Level::kLevel1},
+    {1485, 99, H264Level::kLevel1_b},
+    {3000, 396, H264Level::kLevel1_1},
+    {6000, 396, H264Level::kLevel1_2},
+    {11880, 396, H264Level::kLevel1_3},
+    {11880, 396, H264Level::kLevel2},
+    {19800, 792, H264Level::kLevel2_1},
+    {20250, 1620, H264Level::kLevel2_2},
+    {40500, 1620, H264Level::kLevel3},
+    {108000, 3600, H264Level::kLevel3_1},
+    {216000, 5120, H264Level::kLevel3_2},
+    {245760, 8192, H264Level::kLevel4},
+    {245760, 8192, H264Level::kLevel4_1},
+    {522240, 8704, H264Level::kLevel4_2},
+    {589824, 22080, H264Level::kLevel5},
+    {983040, 36864, H264Level::kLevel5_1},
+    {2073600, 36864, H264Level::kLevel5_2},
+};
+
+}  // anonymous namespace
+
+absl::optional<H264ProfileLevelId> ParseH264ProfileLevelId(const char* str) {
+  // The string should consist of 3 bytes in hexadecimal format.
+  if (strlen(str) != 6u)
+    return absl::nullopt;
+  const uint32_t profile_level_id_numeric = strtol(str, nullptr, 16);
+  if (profile_level_id_numeric == 0)
+    return absl::nullopt;
+
+  // Separate into three bytes.
+  const uint8_t level_idc =
+      static_cast<uint8_t>(profile_level_id_numeric & 0xFF);
+  const uint8_t profile_iop =
+      static_cast<uint8_t>((profile_level_id_numeric >> 8) & 0xFF);
+  const uint8_t profile_idc =
+      static_cast<uint8_t>((profile_level_id_numeric >> 16) & 0xFF);
+
+  // Parse level based on level_idc and constraint set 3 flag.
+  H264Level level_casted = static_cast<H264Level>(level_idc);
+  H264Level level;
+
+  switch (level_casted) {
+    case H264Level::kLevel1_1:
+      level = (profile_iop & kConstraintSet3Flag) != 0 ? H264Level::kLevel1_b
+                                                       : H264Level::kLevel1_1;
+      break;
+    case H264Level::kLevel1:
+    case H264Level::kLevel1_2:
+    case H264Level::kLevel1_3:
+    case H264Level::kLevel2:
+    case H264Level::kLevel2_1:
+    case H264Level::kLevel2_2:
+    case H264Level::kLevel3:
+    case H264Level::kLevel3_1:
+    case H264Level::kLevel3_2:
+    case H264Level::kLevel4:
+    case H264Level::kLevel4_1:
+    case H264Level::kLevel4_2:
+    case H264Level::kLevel5:
+    case H264Level::kLevel5_1:
+    case H264Level::kLevel5_2:
+      level = level_casted;
+      break;
+    default:
+      // Unrecognized level_idc.
+      return absl::nullopt;
+  }
+
+  // Parse profile_idc/profile_iop into a Profile enum.
+  for (const ProfilePattern& pattern : kProfilePatterns) {
+    if (profile_idc == pattern.profile_idc &&
+        pattern.profile_iop.IsMatch(profile_iop)) {
+      return H264ProfileLevelId(pattern.profile, level);
+    }
+  }
+
+  // Unrecognized profile_idc/profile_iop combination.
+  return absl::nullopt;
+}
+
+absl::optional<H264Level> H264SupportedLevel(int max_frame_pixel_count,
+                                             float max_fps) {
+  static const int kPixelsPerMacroblock = 16 * 16;
+
+  for (int i = arraysize(kLevelConstraints) - 1; i >= 0; --i) {
+    const LevelConstraint& level_constraint = kLevelConstraints[i];
+    if (level_constraint.max_macroblock_frame_size * kPixelsPerMacroblock <=
+            max_frame_pixel_count &&
+        level_constraint.max_macroblocks_per_second <=
+            max_fps * level_constraint.max_macroblock_frame_size) {
+      return level_constraint.level;
+    }
+  }
+
+  // No level supported.
+  return absl::nullopt;
+}
+
+absl::optional<H264ProfileLevelId> ParseSdpForH264ProfileLevelId(
+    const SdpVideoFormat::Parameters& params) {
+  // TODO(magjed): The default should really be kProfileBaseline and kLevel1
+  // according to the spec: https://tools.ietf.org/html/rfc6184#section-8.1. In
+  // order to not break backwards compatibility with older versions of WebRTC
+  // where external codecs don't have any parameters, use
+  // kProfileConstrainedBaseline kLevel3_1 instead. This workaround will only be
+  // done in an interim period to allow external clients to update their code.
+  // http://crbug/webrtc/6337.
+  static const H264ProfileLevelId kDefaultProfileLevelId(
+      H264Profile::kProfileConstrainedBaseline, H264Level::kLevel3_1);
+
+  const auto profile_level_id_it = params.find(kProfileLevelId);
+  return (profile_level_id_it == params.end())
+             ? kDefaultProfileLevelId
+             : ParseH264ProfileLevelId(profile_level_id_it->second.c_str());
+}
+
+absl::optional<std::string> H264ProfileLevelIdToString(
+    const H264ProfileLevelId& profile_level_id) {
+  // Handle special case level == 1b.
+  if (profile_level_id.level == H264Level::kLevel1_b) {
+    switch (profile_level_id.profile) {
+      case H264Profile::kProfileConstrainedBaseline:
+        return {"42f00b"};
+      case H264Profile::kProfileBaseline:
+        return {"42100b"};
+      case H264Profile::kProfileMain:
+        return {"4d100b"};
+      // Level 1b is not allowed for other profiles.
+      default:
+        return absl::nullopt;
+    }
+  }
+
+  const char* profile_idc_iop_string;
+  switch (profile_level_id.profile) {
+    case H264Profile::kProfileConstrainedBaseline:
+      profile_idc_iop_string = "42e0";
+      break;
+    case H264Profile::kProfileBaseline:
+      profile_idc_iop_string = "4200";
+      break;
+    case H264Profile::kProfileMain:
+      profile_idc_iop_string = "4d00";
+      break;
+    case H264Profile::kProfileConstrainedHigh:
+      profile_idc_iop_string = "640c";
+      break;
+    case H264Profile::kProfileHigh:
+      profile_idc_iop_string = "6400";
+      break;
+    // Unrecognized profile.
+    default:
+      return absl::nullopt;
+  }
+
+  char str[7];
+  snprintf(str, 7u, "%s%02x", profile_idc_iop_string, profile_level_id.level);
+  return {str};
+}
+
+bool H264IsSameProfile(const SdpVideoFormat::Parameters& params1,
+                       const SdpVideoFormat::Parameters& params2) {
+  const absl::optional<H264ProfileLevelId> profile_level_id =
+      ParseSdpForH264ProfileLevelId(params1);
+  const absl::optional<H264ProfileLevelId> other_profile_level_id =
+      ParseSdpForH264ProfileLevelId(params2);
+  // Compare H264 profiles, but not levels.
+  return profile_level_id && other_profile_level_id &&
+         profile_level_id->profile == other_profile_level_id->profile;
+}
+
+}  // namespace webrtc
diff --git a/api/video_codecs/h264_profile_level_id.h b/api/video_codecs/h264_profile_level_id.h
new file mode 100644
index 0000000..51d025c
--- /dev/null
+++ b/api/video_codecs/h264_profile_level_id.h
@@ -0,0 +1,91 @@
+/*
+ *  Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef API_VIDEO_CODECS_H264_PROFILE_LEVEL_ID_H_
+#define API_VIDEO_CODECS_H264_PROFILE_LEVEL_ID_H_
+
+#include <string>
+
+#include "absl/types/optional.h"
+#include "api/video_codecs/sdp_video_format.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+enum class H264Profile {
+  kProfileConstrainedBaseline,
+  kProfileBaseline,
+  kProfileMain,
+  kProfileConstrainedHigh,
+  kProfileHigh,
+};
+
+// All values are equal to ten times the level number, except level 1b which is
+// special.
+enum class H264Level {
+  kLevel1_b = 0,
+  kLevel1 = 10,
+  kLevel1_1 = 11,
+  kLevel1_2 = 12,
+  kLevel1_3 = 13,
+  kLevel2 = 20,
+  kLevel2_1 = 21,
+  kLevel2_2 = 22,
+  kLevel3 = 30,
+  kLevel3_1 = 31,
+  kLevel3_2 = 32,
+  kLevel4 = 40,
+  kLevel4_1 = 41,
+  kLevel4_2 = 42,
+  kLevel5 = 50,
+  kLevel5_1 = 51,
+  kLevel5_2 = 52
+};
+
+struct H264ProfileLevelId {
+  constexpr H264ProfileLevelId(H264Profile profile, H264Level level)
+      : profile(profile), level(level) {}
+  H264Profile profile;
+  H264Level level;
+};
+
+// Parse profile level id that is represented as a string of 3 hex bytes.
+// Nothing will be returned if the string is not a recognized H264
+// profile level id.
+absl::optional<H264ProfileLevelId> ParseH264ProfileLevelId(const char* str);
+
+// Parse profile level id that is represented as a string of 3 hex bytes
+// contained in an SDP key-value map. A default profile level id will be
+// returned if the profile-level-id key is missing. Nothing will be returned if
+// the key is present but the string is invalid.
+RTC_EXPORT absl::optional<H264ProfileLevelId> ParseSdpForH264ProfileLevelId(
+    const SdpVideoFormat::Parameters& params);
+
+// Given that a decoder supports up to a given frame size (in pixels) at up to a
+// given number of frames per second, return the highest H.264 level where it
+// can guarantee that it will be able to support all valid encoded streams that
+// are within that level.
+RTC_EXPORT absl::optional<H264Level> H264SupportedLevel(
+    int max_frame_pixel_count,
+    float max_fps);
+
+// Returns canonical string representation as three hex bytes of the profile
+// level id, or returns nothing for invalid profile level ids.
+RTC_EXPORT absl::optional<std::string> H264ProfileLevelIdToString(
+    const H264ProfileLevelId& profile_level_id);
+
+// Returns true if the parameters have the same H264 profile (Baseline, High,
+// etc).
+RTC_EXPORT bool H264IsSameProfile(const SdpVideoFormat::Parameters& params1,
+                                  const SdpVideoFormat::Parameters& params2);
+
+}  // namespace webrtc
+
+#endif  // API_VIDEO_CODECS_H264_PROFILE_LEVEL_ID_H_
diff --git a/api/video_codecs/test/BUILD.gn b/api/video_codecs/test/BUILD.gn
index cb810fc..ea8e6d5 100644
--- a/api/video_codecs/test/BUILD.gn
+++ b/api/video_codecs/test/BUILD.gn
@@ -13,6 +13,7 @@
     testonly = true
     sources = [
       "builtin_video_encoder_factory_unittest.cc",
+      "h264_profile_level_id_unittest.cc",
       "video_decoder_software_fallback_wrapper_unittest.cc",
       "video_encoder_software_fallback_wrapper_unittest.cc",
     ]
diff --git a/api/video_codecs/test/h264_profile_level_id_unittest.cc b/api/video_codecs/test/h264_profile_level_id_unittest.cc
new file mode 100644
index 0000000..47098d2
--- /dev/null
+++ b/api/video_codecs/test/h264_profile_level_id_unittest.cc
@@ -0,0 +1,171 @@
+/*
+ *  Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "api/video_codecs/h264_profile_level_id.h"
+
+#include <map>
+#include <string>
+
+#include "absl/types/optional.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+TEST(H264ProfileLevelId, TestParsingInvalid) {
+  // Malformed strings.
+  EXPECT_FALSE(ParseH264ProfileLevelId(""));
+  EXPECT_FALSE(ParseH264ProfileLevelId(" 42e01f"));
+  EXPECT_FALSE(ParseH264ProfileLevelId("4242e01f"));
+  EXPECT_FALSE(ParseH264ProfileLevelId("e01f"));
+  EXPECT_FALSE(ParseH264ProfileLevelId("gggggg"));
+
+  // Invalid level.
+  EXPECT_FALSE(ParseH264ProfileLevelId("42e000"));
+  EXPECT_FALSE(ParseH264ProfileLevelId("42e00f"));
+  EXPECT_FALSE(ParseH264ProfileLevelId("42e0ff"));
+
+  // Invalid profile.
+  EXPECT_FALSE(ParseH264ProfileLevelId("42e11f"));
+  EXPECT_FALSE(ParseH264ProfileLevelId("58601f"));
+  EXPECT_FALSE(ParseH264ProfileLevelId("64e01f"));
+}
+
+TEST(H264ProfileLevelId, TestParsingLevel) {
+  EXPECT_EQ(H264Level::kLevel3_1, ParseH264ProfileLevelId("42e01f")->level);
+  EXPECT_EQ(H264Level::kLevel1_1, ParseH264ProfileLevelId("42e00b")->level);
+  EXPECT_EQ(H264Level::kLevel1_b, ParseH264ProfileLevelId("42f00b")->level);
+  EXPECT_EQ(H264Level::kLevel4_2, ParseH264ProfileLevelId("42C02A")->level);
+  EXPECT_EQ(H264Level::kLevel5_2, ParseH264ProfileLevelId("640c34")->level);
+}
+
+TEST(H264ProfileLevelId, TestParsingConstrainedBaseline) {
+  EXPECT_EQ(H264Profile::kProfileConstrainedBaseline,
+            ParseH264ProfileLevelId("42e01f")->profile);
+  EXPECT_EQ(H264Profile::kProfileConstrainedBaseline,
+            ParseH264ProfileLevelId("42C02A")->profile);
+  EXPECT_EQ(H264Profile::kProfileConstrainedBaseline,
+            ParseH264ProfileLevelId("4de01f")->profile);
+  EXPECT_EQ(H264Profile::kProfileConstrainedBaseline,
+            ParseH264ProfileLevelId("58f01f")->profile);
+}
+
+TEST(H264ProfileLevelId, TestParsingBaseline) {
+  EXPECT_EQ(H264Profile::kProfileBaseline,
+            ParseH264ProfileLevelId("42a01f")->profile);
+  EXPECT_EQ(H264Profile::kProfileBaseline,
+            ParseH264ProfileLevelId("58A01F")->profile);
+}
+
+TEST(H264ProfileLevelId, TestParsingMain) {
+  EXPECT_EQ(H264Profile::kProfileMain,
+            ParseH264ProfileLevelId("4D401f")->profile);
+}
+
+TEST(H264ProfileLevelId, TestParsingHigh) {
+  EXPECT_EQ(H264Profile::kProfileHigh,
+            ParseH264ProfileLevelId("64001f")->profile);
+}
+
+TEST(H264ProfileLevelId, TestParsingConstrainedHigh) {
+  EXPECT_EQ(H264Profile::kProfileConstrainedHigh,
+            ParseH264ProfileLevelId("640c1f")->profile);
+}
+
+TEST(H264ProfileLevelId, TestSupportedLevel) {
+  EXPECT_EQ(H264Level::kLevel2_1, *H264SupportedLevel(640 * 480, 25));
+  EXPECT_EQ(H264Level::kLevel3_1, *H264SupportedLevel(1280 * 720, 30));
+  EXPECT_EQ(H264Level::kLevel4_2, *H264SupportedLevel(1920 * 1280, 60));
+}
+
+// Test supported level below level 1 requirements.
+TEST(H264ProfileLevelId, TestSupportedLevelInvalid) {
+  EXPECT_FALSE(H264SupportedLevel(0, 0));
+  // All levels support fps > 5.
+  EXPECT_FALSE(H264SupportedLevel(1280 * 720, 5));
+  // All levels support frame sizes > 183 * 137.
+  EXPECT_FALSE(H264SupportedLevel(183 * 137, 30));
+}
+
+TEST(H264ProfileLevelId, TestToString) {
+  EXPECT_EQ("42e01f", *H264ProfileLevelIdToString(H264ProfileLevelId(
+                          H264Profile::kProfileConstrainedBaseline,
+                          H264Level::kLevel3_1)));
+  EXPECT_EQ("42000a", *H264ProfileLevelIdToString(H264ProfileLevelId(
+                          H264Profile::kProfileBaseline, H264Level::kLevel1)));
+  EXPECT_EQ("4d001f", H264ProfileLevelIdToString(H264ProfileLevelId(
+                          H264Profile::kProfileMain, H264Level::kLevel3_1)));
+  EXPECT_EQ("640c2a",
+            *H264ProfileLevelIdToString(H264ProfileLevelId(
+                H264Profile::kProfileConstrainedHigh, H264Level::kLevel4_2)));
+  EXPECT_EQ("64002a", *H264ProfileLevelIdToString(H264ProfileLevelId(
+                          H264Profile::kProfileHigh, H264Level::kLevel4_2)));
+}
+
+TEST(H264ProfileLevelId, TestToStringLevel1b) {
+  EXPECT_EQ("42f00b", *H264ProfileLevelIdToString(H264ProfileLevelId(
+                          H264Profile::kProfileConstrainedBaseline,
+                          H264Level::kLevel1_b)));
+  EXPECT_EQ("42100b",
+            *H264ProfileLevelIdToString(H264ProfileLevelId(
+                H264Profile::kProfileBaseline, H264Level::kLevel1_b)));
+  EXPECT_EQ("4d100b", *H264ProfileLevelIdToString(H264ProfileLevelId(
+                          H264Profile::kProfileMain, H264Level::kLevel1_b)));
+}
+
+TEST(H264ProfileLevelId, TestToStringRoundTrip) {
+  EXPECT_EQ("42e01f",
+            *H264ProfileLevelIdToString(*ParseH264ProfileLevelId("42e01f")));
+  EXPECT_EQ("42e01f",
+            *H264ProfileLevelIdToString(*ParseH264ProfileLevelId("42E01F")));
+  EXPECT_EQ("4d100b",
+            *H264ProfileLevelIdToString(*ParseH264ProfileLevelId("4d100b")));
+  EXPECT_EQ("4d100b",
+            *H264ProfileLevelIdToString(*ParseH264ProfileLevelId("4D100B")));
+  EXPECT_EQ("640c2a",
+            *H264ProfileLevelIdToString(*ParseH264ProfileLevelId("640c2a")));
+  EXPECT_EQ("640c2a",
+            *H264ProfileLevelIdToString(*ParseH264ProfileLevelId("640C2A")));
+}
+
+TEST(H264ProfileLevelId, TestToStringInvalid) {
+  EXPECT_FALSE(H264ProfileLevelIdToString(
+      H264ProfileLevelId(H264Profile::kProfileHigh, H264Level::kLevel1_b)));
+  EXPECT_FALSE(H264ProfileLevelIdToString(H264ProfileLevelId(
+      H264Profile::kProfileConstrainedHigh, H264Level::kLevel1_b)));
+  EXPECT_FALSE(H264ProfileLevelIdToString(
+      H264ProfileLevelId(static_cast<H264Profile>(255), H264Level::kLevel3_1)));
+}
+
+TEST(H264ProfileLevelId, TestParseSdpProfileLevelIdEmpty) {
+  const absl::optional<H264ProfileLevelId> profile_level_id =
+      ParseSdpForH264ProfileLevelId(SdpVideoFormat::Parameters());
+  EXPECT_TRUE(profile_level_id);
+  EXPECT_EQ(H264Profile::kProfileConstrainedBaseline,
+            profile_level_id->profile);
+  EXPECT_EQ(H264Level::kLevel3_1, profile_level_id->level);
+}
+
+TEST(H264ProfileLevelId, TestParseSdpProfileLevelIdConstrainedHigh) {
+  SdpVideoFormat::Parameters params;
+  params["profile-level-id"] = "640c2a";
+  const absl::optional<H264ProfileLevelId> profile_level_id =
+      ParseSdpForH264ProfileLevelId(params);
+  EXPECT_TRUE(profile_level_id);
+  EXPECT_EQ(H264Profile::kProfileConstrainedHigh, profile_level_id->profile);
+  EXPECT_EQ(H264Level::kLevel4_2, profile_level_id->level);
+}
+
+TEST(H264ProfileLevelId, TestParseSdpProfileLevelIdInvalid) {
+  SdpVideoFormat::Parameters params;
+  params["profile-level-id"] = "foobar";
+  EXPECT_FALSE(ParseSdpForH264ProfileLevelId(params));
+}
+
+}  // namespace webrtc
diff --git a/api/video_codecs/vp9_profile.cc b/api/video_codecs/vp9_profile.cc
new file mode 100644
index 0000000..d69f566
--- /dev/null
+++ b/api/video_codecs/vp9_profile.cc
@@ -0,0 +1,70 @@
+/*
+ *  Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "api/video_codecs/vp9_profile.h"
+
+#include <map>
+#include <utility>
+
+#include "rtc_base/string_to_number.h"
+
+namespace webrtc {
+
+// Profile information for VP9 video.
+const char kVP9FmtpProfileId[] = "profile-id";
+
+std::string VP9ProfileToString(VP9Profile profile) {
+  switch (profile) {
+    case VP9Profile::kProfile0:
+      return "0";
+    case VP9Profile::kProfile1:
+      return "1";
+    case VP9Profile::kProfile2:
+      return "2";
+  }
+  return "0";
+}
+
+absl::optional<VP9Profile> StringToVP9Profile(const std::string& str) {
+  const absl::optional<int> i = rtc::StringToNumber<int>(str);
+  if (!i.has_value())
+    return absl::nullopt;
+
+  switch (i.value()) {
+    case 0:
+      return VP9Profile::kProfile0;
+    case 1:
+      return VP9Profile::kProfile1;
+    case 2:
+      return VP9Profile::kProfile2;
+    default:
+      return absl::nullopt;
+  }
+  return absl::nullopt;
+}
+
+absl::optional<VP9Profile> ParseSdpForVP9Profile(
+    const SdpVideoFormat::Parameters& params) {
+  const auto profile_it = params.find(kVP9FmtpProfileId);
+  if (profile_it == params.end())
+    return VP9Profile::kProfile0;
+  const std::string& profile_str = profile_it->second;
+  return StringToVP9Profile(profile_str);
+}
+
+bool VP9IsSameProfile(const SdpVideoFormat::Parameters& params1,
+                      const SdpVideoFormat::Parameters& params2) {
+  const absl::optional<VP9Profile> profile = ParseSdpForVP9Profile(params1);
+  const absl::optional<VP9Profile> other_profile =
+      ParseSdpForVP9Profile(params2);
+  return profile && other_profile && profile == other_profile;
+}
+
+}  // namespace webrtc
diff --git a/api/video_codecs/vp9_profile.h b/api/video_codecs/vp9_profile.h
new file mode 100644
index 0000000..e632df4
--- /dev/null
+++ b/api/video_codecs/vp9_profile.h
@@ -0,0 +1,53 @@
+/*
+ *  Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef API_VIDEO_CODECS_VP9_PROFILE_H_
+#define API_VIDEO_CODECS_VP9_PROFILE_H_
+
+#include <string>
+
+#include "absl/types/optional.h"
+#include "api/video_codecs/sdp_video_format.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+// Profile information for VP9 video.
+extern RTC_EXPORT const char kVP9FmtpProfileId[];
+
+enum class VP9Profile {
+  kProfile0,
+  kProfile1,
+  kProfile2,
+};
+
+// Helper functions to convert VP9Profile to std::string. Returns "0" by
+// default.
+RTC_EXPORT std::string VP9ProfileToString(VP9Profile profile);
+
+// Helper functions to convert std::string to VP9Profile. Returns null if given
+// an invalid profile string.
+absl::optional<VP9Profile> StringToVP9Profile(const std::string& str);
+
+// Parse profile that is represented as a string of single digit contained in an
+// SDP key-value map. A default profile(kProfile0) will be returned if the
+// profile key is missing. Nothing will be returned if the key is present but
+// the string is invalid.
+RTC_EXPORT absl::optional<VP9Profile> ParseSdpForVP9Profile(
+    const SdpVideoFormat::Parameters& params);
+
+// Returns true if the parameters have the same VP9 profile, or neither contains
+// VP9 profile.
+bool VP9IsSameProfile(const SdpVideoFormat::Parameters& params1,
+                      const SdpVideoFormat::Parameters& params2);
+
+}  // namespace webrtc
+
+#endif  // API_VIDEO_CODECS_VP9_PROFILE_H_