Extract CreateH264ConstrainedBaselineProfile

Extracts the logic for generating H.264 Constrained Baseline Profile
(CBP) SDP formats into a standalone method.

This is a preparatory refactoring for an upcoming Chromium CL
(crrev.com/c/7929853), which will reuse this logic to retain the highest
supported H.264 level when deduplicating video encoder capabilities.

Bug: webrtc:521921838
Change-Id: Ibe05d6a807ec148ffe37e475ce19e9f1bacef3d1
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/482340
Reviewed-by: Erik Språng <sprang@webrtc.org>
Commit-Queue: Johannes Kron <kron@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#48008}
diff --git a/media/base/codec.cc b/media/base/codec.cc
index 480f74b..8b7da35 100644
--- a/media/base/codec.cc
+++ b/media/base/codec.cc
@@ -15,6 +15,7 @@
 #include <iterator>
 #include <optional>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "absl/algorithm/container.h"
@@ -367,34 +368,43 @@
   return result;
 }
 
+std::optional<SdpVideoFormat> CreateH264ConstrainedBaselineProfile(
+    const SdpVideoFormat& format) {
+  if (format.name != kH264CodecName) {
+    return std::nullopt;
+  }
+  const std::optional<H264ProfileLevelId> profile_level_id =
+      ParseSdpForH264ProfileLevelId(format.parameters);
+  if (!profile_level_id ||
+      profile_level_id->profile == H264Profile::kProfileConstrainedBaseline) {
+    return std::nullopt;
+  }
+  SdpVideoFormat cbp_format = format;
+  H264ProfileLevelId cbp_profile = *profile_level_id;
+  cbp_profile.profile = H264Profile::kProfileConstrainedBaseline;
+  cbp_format.parameters[kH264FmtpProfileLevelId] =
+      *H264ProfileLevelIdToString(cbp_profile);
+  return cbp_format;
+}
+
 // If a decoder supports any H264 profile, it is implicitly assumed to also
 // support constrained base line even though it's not explicitly listed.
 void AddH264ConstrainedBaselineProfileToSupportedFormats(
     std::vector<SdpVideoFormat>* supported_formats) {
-  std::vector<SdpVideoFormat> cbr_supported_formats;
+  std::vector<SdpVideoFormat> cbp_supported_formats;
 
   // For any H264 supported profile, add the corresponding constrained baseline
   // profile.
-  for (auto it = supported_formats->cbegin(); it != supported_formats->cend();
-       ++it) {
-    if (it->name == kH264CodecName) {
-      const std::optional<H264ProfileLevelId> profile_level_id =
-          ParseSdpForH264ProfileLevelId(it->parameters);
-      if (profile_level_id && profile_level_id->profile !=
-                                  H264Profile::kProfileConstrainedBaseline) {
-        SdpVideoFormat cbp_format = *it;
-        H264ProfileLevelId cbp_profile = *profile_level_id;
-        cbp_profile.profile = H264Profile::kProfileConstrainedBaseline;
-        cbp_format.parameters[kH264FmtpProfileLevelId] =
-            *H264ProfileLevelIdToString(cbp_profile);
-        cbr_supported_formats.push_back(cbp_format);
-      }
+  for (const SdpVideoFormat& format : *supported_formats) {
+    if (std::optional<SdpVideoFormat> cbp_format =
+            CreateH264ConstrainedBaselineProfile(format)) {
+      cbp_supported_formats.push_back(std::move(*cbp_format));
     }
   }
 
   size_t original_size = supported_formats->size();
   // ...if it's not already in the list.
-  std::copy_if(cbr_supported_formats.begin(), cbr_supported_formats.end(),
+  std::copy_if(cbp_supported_formats.begin(), cbp_supported_formats.end(),
                std::back_inserter(*supported_formats),
                [supported_formats](const SdpVideoFormat& format) {
                  return !format.IsCodecInList(*supported_formats);
diff --git a/media/base/codec.h b/media/base/codec.h
index 31f5999..6d5f23e 100644
--- a/media/base/codec.h
+++ b/media/base/codec.h
@@ -272,6 +272,12 @@
     const std::vector<Codec>& supported_codecs,
     const Codec& codec);
 
+// Returns a copy of `format` modified to be H.264 Constrained Baseline Profile
+// (CBP) if `format` is H.264, has a valid profile-level-id, and is not already
+// CBP. Otherwise, returns std::nullopt.
+RTC_EXPORT std::optional<SdpVideoFormat> CreateH264ConstrainedBaselineProfile(
+    const SdpVideoFormat& format);
+
 RTC_EXPORT void AddH264ConstrainedBaselineProfileToSupportedFormats(
     std::vector<SdpVideoFormat>* supported_formats);
 
diff --git a/media/base/codec_unittest.cc b/media/base/codec_unittest.cc
index 231198d..0e5973e 100644
--- a/media/base/codec_unittest.cc
+++ b/media/base/codec_unittest.cc
@@ -333,6 +333,34 @@
   EXPECT_EQ(supported_formats.size(), kExplicitlySupportedFormats.size());
 }
 
+TEST(CodecTest, CreateH264ConstrainedBaselineProfileReturnsCbpForBaseline) {
+  SdpVideoFormat baseline_format = CreateH264Format(
+      H264Profile::kProfileBaseline, H264Level::kLevel3_1, "1");
+  SdpVideoFormat expected_cbp_format = CreateH264Format(
+      H264Profile::kProfileConstrainedBaseline, H264Level::kLevel3_1, "1");
+
+  std::optional<SdpVideoFormat> cbp_format =
+      CreateH264ConstrainedBaselineProfile(baseline_format);
+
+  ASSERT_TRUE(cbp_format.has_value());
+  EXPECT_EQ(*cbp_format, expected_cbp_format);
+}
+
+TEST(CodecTest, CreateH264ConstrainedBaselineProfileReturnsNulloptForCbp) {
+  SdpVideoFormat cbp_format = CreateH264Format(
+      H264Profile::kProfileConstrainedBaseline, H264Level::kLevel3_1, "1");
+
+  EXPECT_FALSE(CreateH264ConstrainedBaselineProfile(cbp_format).has_value());
+}
+
+TEST(CodecTest, CreateH264ConstrainedBaselineProfileReturnsNulloptForVp9) {
+  SdpVideoFormat vp9_format = {
+      kVp9CodecName,
+      {{kVP9FmtpProfileId, VP9ProfileToString(VP9Profile::kProfile0)}}};
+
+  EXPECT_FALSE(CreateH264ConstrainedBaselineProfile(vp9_format).has_value());
+}
+
 TEST(CodecTest, AbslStringify) {
   Codec codec = CreateAudioCodec(47, "custom-audio", 48000, 2);
   EXPECT_EQ(absl::StrCat(codec), "[47:audio/custom-audio/48000/2]");