Add VP9 profile negotiation to SDP

This CL adds VP9 profile information in SDP. It adds the necessary fields and
enums to codec containers.

Additional profiles will be followed.

Bug: webrtc:9376
Change-Id: I78574714f06f8087262a71dd64c01f31a229dd54
Reviewed-on: https://webrtc-review.googlesource.com/81960
Reviewed-by: Taylor (left Google) <deadbeef@webrtc.org>
Reviewed-by: Niklas Enbom <niklas.enbom@webrtc.org>
Reviewed-by: Rasmus Brandt <brandtr@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Commit-Queue: Emircan Uysaler <emircan@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#23810}
diff --git a/media/BUILD.gn b/media/BUILD.gn
index f932a89..03b82c6 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -53,6 +53,20 @@
   ]
 }
 
+rtc_source_set("rtc_vp9_profile") {
+  sources = [
+    "base/vp9_profile.cc",
+    "base/vp9_profile.h",
+  ]
+
+  deps = [
+    "..:webrtc_common",
+    "../api/video_codecs:video_codecs_api",
+    "../rtc_base:rtc_base_approved",
+    "//third_party/abseil-cpp/absl/types:optional",
+  ]
+}
+
 rtc_static_library("rtc_media_base") {
   visibility = [ "*" ]
   defines = []
@@ -109,6 +123,7 @@
   deps += [
     ":rtc_h264_profile_id",
     ":rtc_media_config",
+    ":rtc_vp9_profile",
     "..:webrtc_common",
     "../api:libjingle_peerconnection_api",
     "../api/audio_codecs:audio_codecs_api",
@@ -551,6 +566,7 @@
       ":rtc_media",
       ":rtc_media_base",
       ":rtc_media_tests_utils",
+      ":rtc_vp9_profile",
       "../api:create_simulcast_test_fixture_api",
       "../api:libjingle_peerconnection_api",
       "../api:mock_video_codec_factory",
diff --git a/media/base/codec.cc b/media/base/codec.cc
index 7c1d677..caf3212 100644
--- a/media/base/codec.cc
+++ b/media/base/codec.cc
@@ -13,6 +13,7 @@
 #include <algorithm>
 
 #include "media/base/h264_profile_level_id.h"
+#include "media/base/vp9_profile.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/logging.h"
 #include "rtc_base/stringencode.h"
@@ -270,6 +271,8 @@
   if (CodecNamesEq(name.c_str(), kH264CodecName))
     return webrtc::H264::IsSameH264Profile(params, other.params) &&
            IsSameH264PacketizationMode(params, other.params);
+  if (CodecNamesEq(name.c_str(), kVp9CodecName))
+    return webrtc::IsSameVP9Profile(params, other.params);
   return true;
 }
 
@@ -386,9 +389,12 @@
   // If different names (case insensitive), then not same formats.
   if (!CodecNamesEq(name1, name2))
     return false;
-  // For every format besides H264, comparing names is enough.
-  return !CodecNamesEq(name1.c_str(), kH264CodecName) ||
-         webrtc::H264::IsSameH264Profile(params1, params2);
+  // For every format besides H264 and VP9, comparing names is enough.
+  if (CodecNamesEq(name1.c_str(), kH264CodecName))
+    return webrtc::H264::IsSameH264Profile(params1, params2);
+  if (CodecNamesEq(name1.c_str(), kVp9CodecName))
+    return webrtc::IsSameVP9Profile(params1, params2);
+  return true;
 }
 
 }  // namespace cricket
diff --git a/media/base/codec_unittest.cc b/media/base/codec_unittest.cc
index 36e0c38..54396a9 100644
--- a/media/base/codec_unittest.cc
+++ b/media/base/codec_unittest.cc
@@ -9,6 +9,8 @@
  */
 
 #include "media/base/codec.h"
+
+#include "media/base/vp9_profile.h"
 #include "rtc_base/gunit.h"
 
 using cricket::AudioCodec;
@@ -186,6 +188,36 @@
   EXPECT_FALSE(c1.Matches(VideoCodec(95, "V")));
 }
 
+// VP9 codecs compare profile information.
+TEST(CodecTest, TestVP9CodecMatches) {
+  const char kProfile0[] = "0";
+  const char kProfile2[] = "2";
+
+  VideoCodec c_no_profile(95, cricket::kVp9CodecName);
+  VideoCodec c_profile0(95, cricket::kVp9CodecName);
+  c_profile0.params[webrtc::kVP9FmtpProfileId] = kProfile0;
+
+  EXPECT_TRUE(c_profile0.Matches(c_no_profile));
+
+  {
+    VideoCodec c_profile0_eq(95, cricket::kVp9CodecName);
+    c_profile0_eq.params[webrtc::kVP9FmtpProfileId] = kProfile0;
+    EXPECT_TRUE(c_profile0.Matches(c_profile0_eq));
+  }
+
+  {
+    VideoCodec c_profile2(95, cricket::kVp9CodecName);
+    c_profile2.params[webrtc::kVP9FmtpProfileId] = kProfile2;
+    EXPECT_FALSE(c_profile0.Matches(c_profile2));
+    EXPECT_FALSE(c_no_profile.Matches(c_profile2));
+  }
+
+  {
+    VideoCodec c_no_profile_eq(95, cricket::kVp9CodecName);
+    EXPECT_TRUE(c_no_profile.Matches(c_no_profile_eq));
+  }
+}
+
 // Matching H264 codecs also need to have matching profile-level-id and
 // packetization-mode.
 TEST(CodecTest, TestH264CodecMatches) {
diff --git a/media/base/vp9_profile.cc b/media/base/vp9_profile.cc
new file mode 100644
index 0000000..3ced430
--- /dev/null
+++ b/media/base/vp9_profile.cc
@@ -0,0 +1,63 @@
+/*
+ *  Copyright (c) 2018 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 "media/base/vp9_profile.h"
+
+#include "rtc_base/string_to_number.h"
+
+namespace webrtc {
+
+// Profile information for VP9 video.
+const char kVP9FmtpProfileId[] = "x-google-profile-id";
+
+std::string VP9ProfileToString(VP9Profile profile) {
+  switch (profile) {
+    case VP9Profile::kProfile0:
+      return "0";
+    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 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 IsSameVP9Profile(const SdpVideoFormat::Parameters& params1,
+                      const SdpVideoFormat::Parameters& params2) {
+  const rtc::Optional<VP9Profile> profile = ParseSdpForVP9Profile(params1);
+  const rtc::Optional<VP9Profile> other_profile =
+      ParseSdpForVP9Profile(params2);
+  return profile && other_profile && profile == other_profile;
+}
+
+}  // namespace webrtc
diff --git a/media/base/vp9_profile.h b/media/base/vp9_profile.h
new file mode 100644
index 0000000..5cc1d16
--- /dev/null
+++ b/media/base/vp9_profile.h
@@ -0,0 +1,53 @@
+/*
+ *  Copyright (c) 2018 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 MEDIA_BASE_VP9_PROFILE_H_
+#define MEDIA_BASE_VP9_PROFILE_H_
+
+#include <map>
+#include <string>
+
+#include "absl/types/optional.h"
+#include "api/video_codecs/sdp_video_format.h"
+#include "common_types.h"  // NOLINT(build/include)
+
+namespace webrtc {
+
+// Profile information for VP9 video.
+extern const char kVP9FmtpProfileId[];
+
+enum class VP9Profile {
+  kProfile0,
+  kProfile2,
+};
+
+// Helper functions to convert VP9Profile to std::string. Returns "0" by
+// default.
+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.
+absl::optional<VP9Profile> ParseSdpForVP9Profile(
+    const SdpVideoFormat::Parameters& params);
+
+// Returns true if the parameters have the same VP9 profile, or neither contains
+// VP9 profile.
+bool IsSameVP9Profile(const SdpVideoFormat::Parameters& params1,
+                      const SdpVideoFormat::Parameters& params2);
+
+}  // namespace webrtc
+
+#endif  // MEDIA_BASE_VP9_PROFILE_H_
diff --git a/media/engine/internaldecoderfactory.cc b/media/engine/internaldecoderfactory.cc
index c9cd861..08e29cf 100644
--- a/media/engine/internaldecoderfactory.cc
+++ b/media/engine/internaldecoderfactory.cc
@@ -24,8 +24,8 @@
     const {
   std::vector<SdpVideoFormat> formats;
   formats.push_back(SdpVideoFormat(cricket::kVp8CodecName));
-  if (VP9Decoder::IsSupported())
-    formats.push_back(SdpVideoFormat(cricket::kVp9CodecName));
+  for (const SdpVideoFormat& format : SupportedVP9Codecs())
+    formats.push_back(format);
   for (const SdpVideoFormat& h264_format : SupportedH264Codecs())
     formats.push_back(h264_format);
   return formats;
@@ -35,15 +35,10 @@
     const SdpVideoFormat& format) {
   if (cricket::CodecNamesEq(format.name, cricket::kVp8CodecName))
     return VP8Decoder::Create();
-
-  if (cricket::CodecNamesEq(format.name, cricket::kVp9CodecName)) {
-    RTC_DCHECK(VP9Decoder::IsSupported());
+  if (cricket::CodecNamesEq(format.name, cricket::kVp9CodecName))
     return VP9Decoder::Create();
-  }
-
   if (cricket::CodecNamesEq(format.name, cricket::kH264CodecName))
     return H264Decoder::Create();
-
   RTC_LOG(LS_ERROR) << "Trying to create decoder for unsupported format";
   return nullptr;
 }
diff --git a/media/engine/internalencoderfactory.cc b/media/engine/internalencoderfactory.cc
index 2f7df9f..f980c7d 100644
--- a/media/engine/internalencoderfactory.cc
+++ b/media/engine/internalencoderfactory.cc
@@ -24,12 +24,10 @@
     const {
   std::vector<SdpVideoFormat> supported_codecs;
   supported_codecs.push_back(SdpVideoFormat(cricket::kVp8CodecName));
-  if (webrtc::VP9Encoder::IsSupported())
-    supported_codecs.push_back(SdpVideoFormat(cricket::kVp9CodecName));
-
+  for (const webrtc::SdpVideoFormat& format : webrtc::SupportedVP9Codecs())
+    supported_codecs.push_back(format);
   for (const webrtc::SdpVideoFormat& format : webrtc::SupportedH264Codecs())
     supported_codecs.push_back(format);
-
   return supported_codecs;
 }
 
@@ -45,13 +43,10 @@
     const SdpVideoFormat& format) {
   if (cricket::CodecNamesEq(format.name, cricket::kVp8CodecName))
     return VP8Encoder::Create();
-
   if (cricket::CodecNamesEq(format.name, cricket::kVp9CodecName))
     return VP9Encoder::Create();
-
   if (cricket::CodecNamesEq(format.name, cricket::kH264CodecName))
     return H264Encoder::Create(cricket::VideoCodec(format));
-
   RTC_LOG(LS_ERROR) << "Trying to created encoder of unsupported format "
                     << format.name;
   return nullptr;
diff --git a/modules/video_coding/BUILD.gn b/modules/video_coding/BUILD.gn
index bff40c9..68d4426 100644
--- a/modules/video_coding/BUILD.gn
+++ b/modules/video_coding/BUILD.gn
@@ -472,7 +472,10 @@
     ":webrtc_vp9_helpers",
     "..:module_api",
     "../..:webrtc_common",
+    "../../api/video_codecs:video_codecs_api",
     "../../common_video",
+    "../../media:rtc_media_base",
+    "../../media:rtc_vp9_profile",
     "../../rtc_base:checks",
     "../../rtc_base:rtc_base",
     "../../system_wrappers",
diff --git a/modules/video_coding/codecs/vp9/DEPS b/modules/video_coding/codecs/vp9/DEPS
new file mode 100644
index 0000000..cc5cd70
--- /dev/null
+++ b/modules/video_coding/codecs/vp9/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+media/base",
+]
diff --git a/modules/video_coding/codecs/vp9/include/vp9.h b/modules/video_coding/codecs/vp9/include/vp9.h
index 39dc138..8091cac 100644
--- a/modules/video_coding/codecs/vp9/include/vp9.h
+++ b/modules/video_coding/codecs/vp9/include/vp9.h
@@ -13,22 +13,31 @@
 #define MODULES_VIDEO_CODING_CODECS_VP9_INCLUDE_VP9_H_
 
 #include <memory>
+#include <vector>
 
+#include "api/video_codecs/sdp_video_format.h"
+#include "media/base/codec.h"
 #include "modules/video_coding/include/video_codec_interface.h"
 
 namespace webrtc {
 
+// Returns a vector with all supported internal VP9 profiles that we can
+// negotiate in SDP, in order of preference.
+std::vector<SdpVideoFormat> SupportedVP9Codecs();
+
 class VP9Encoder : public VideoEncoder {
  public:
-  static bool IsSupported();
+  // Deprecated. Returns default implementation using VP9 Profile 0.
+  // TODO(emircan): Remove once this is no longer used.
   static std::unique_ptr<VP9Encoder> Create();
+  // Parses VP9 Profile from |codec| and returns the appropriate implementation.
+  static std::unique_ptr<VP9Encoder> Create(const cricket::VideoCodec& codec);
 
   ~VP9Encoder() override {}
 };
 
 class VP9Decoder : public VideoDecoder {
  public:
-  static bool IsSupported();
   static std::unique_ptr<VP9Decoder> Create();
 
   ~VP9Decoder() override {}
diff --git a/modules/video_coding/codecs/vp9/vp9_impl.cc b/modules/video_coding/codecs/vp9/vp9_impl.cc
index 5b7f5b9..39ab7b3 100644
--- a/modules/video_coding/codecs/vp9/vp9_impl.cc
+++ b/modules/video_coding/codecs/vp9/vp9_impl.cc
@@ -52,12 +52,19 @@
 #endif
 }
 
-bool VP9Encoder::IsSupported() {
-  return true;
+std::vector<SdpVideoFormat> SupportedVP9Codecs() {
+  return {SdpVideoFormat(
+      cricket::kVp9CodecName,
+      {{kVP9FmtpProfileId, VP9ProfileToString(VP9Profile::kProfile0)}})};
 }
 
 std::unique_ptr<VP9Encoder> VP9Encoder::Create() {
-  return rtc::MakeUnique<VP9EncoderImpl>();
+  return rtc::MakeUnique<VP9EncoderImpl>(cricket::VideoCodec());
+}
+
+std::unique_ptr<VP9Encoder> VP9Encoder::Create(
+    const cricket::VideoCodec& codec) {
+  return rtc::MakeUnique<VP9EncoderImpl>(codec);
 }
 
 void VP9EncoderImpl::EncoderOutputCodedPacketCallback(vpx_codec_cx_pkt* pkt,
@@ -66,9 +73,11 @@
   enc->GetEncodedLayerFrame(pkt);
 }
 
-VP9EncoderImpl::VP9EncoderImpl()
+VP9EncoderImpl::VP9EncoderImpl(const cricket::VideoCodec& codec)
     : encoded_image_(),
       encoded_complete_callback_(nullptr),
+      profile_(
+          ParseSdpForVP9Profile(codec.params).value_or(VP9Profile::kProfile0)),
       inited_(false),
       timestamp_(0),
       cpu_speed_(3),
@@ -88,6 +97,7 @@
       is_flexible_mode_(false) {
   memset(&codec_, 0, sizeof(codec_));
   memset(&svc_params_, 0, sizeof(vpx_svc_extra_cfg_t));
+  RTC_DCHECK(VP9Profile::kProfile0 == profile_);
 }
 
 VP9EncoderImpl::~VP9EncoderImpl() {
@@ -960,10 +970,6 @@
   return "libvpx";
 }
 
-bool VP9Decoder::IsSupported() {
-  return true;
-}
-
 std::unique_ptr<VP9Decoder> VP9Decoder::Create() {
   return rtc::MakeUnique<VP9DecoderImpl>();
 }
diff --git a/modules/video_coding/codecs/vp9/vp9_impl.h b/modules/video_coding/codecs/vp9/vp9_impl.h
index 5cf09a3..7009311 100644
--- a/modules/video_coding/codecs/vp9/vp9_impl.h
+++ b/modules/video_coding/codecs/vp9/vp9_impl.h
@@ -17,6 +17,8 @@
 #include <vector>
 
 #include "modules/video_coding/codecs/vp9/include/vp9.h"
+
+#include "media/base/vp9_profile.h"
 #include "modules/video_coding/codecs/vp9/vp9_frame_buffer_pool.h"
 #include "rtc_base/rate_statistics.h"
 
@@ -28,7 +30,7 @@
 
 class VP9EncoderImpl : public VP9Encoder {
  public:
-  VP9EncoderImpl();
+  explicit VP9EncoderImpl(const cricket::VideoCodec& codec);
 
   virtual ~VP9EncoderImpl();
 
@@ -94,6 +96,7 @@
   CodecSpecificInfo codec_specific_;
   EncodedImageCallback* encoded_complete_callback_;
   VideoCodec codec_;
+  const VP9Profile profile_;
   bool inited_;
   int64_t timestamp_;
   int cpu_speed_;
diff --git a/modules/video_coding/codecs/vp9/vp9_noop.cc b/modules/video_coding/codecs/vp9/vp9_noop.cc
index baa0486..b058c2d 100644
--- a/modules/video_coding/codecs/vp9/vp9_noop.cc
+++ b/modules/video_coding/codecs/vp9/vp9_noop.cc
@@ -14,12 +14,14 @@
 #endif  // !defined(RTC_DISABLE_VP9)
 
 #include "modules/video_coding/codecs/vp9/include/vp9.h"
+
+#include "api/video_codecs/sdp_video_format.h"
 #include "rtc_base/checks.h"
 
 namespace webrtc {
 
-bool VP9Encoder::IsSupported() {
-  return false;
+std::vector<SdpVideoFormat> SupportedVP9Codecs() {
+  return std::vector<SdpVideoFormat>();
 }
 
 std::unique_ptr<VP9Encoder> VP9Encoder::Create() {
@@ -27,8 +29,10 @@
   return nullptr;
 }
 
-bool VP9Decoder::IsSupported() {
-  return false;
+std::unique_ptr<VP9Encoder> VP9Encoder::Create(
+    const cricket::VideoCodec& codec) {
+  RTC_NOTREACHED();
+  return nullptr;
 }
 
 std::unique_ptr<VP9Decoder> VP9Decoder::Create() {
diff --git a/sdk/android/src/jni/vp9codec.cc b/sdk/android/src/jni/vp9codec.cc
index d9cc387..ca712e6 100644
--- a/sdk/android/src/jni/vp9codec.cc
+++ b/sdk/android/src/jni/vp9codec.cc
@@ -25,7 +25,7 @@
 
 static jboolean JNI_VP9Encoder_IsSupported(JNIEnv* jni,
                                            const JavaParamRef<jclass>&) {
-  return VP9Encoder::IsSupported();
+  return !SupportedVP9Codecs().empty();
 }
 
 static jlong JNI_VP9Decoder_CreateDecoder(JNIEnv* jni,
@@ -35,7 +35,7 @@
 
 static jboolean JNI_VP9Decoder_IsSupported(JNIEnv* jni,
                                            const JavaParamRef<jclass>&) {
-  return VP9Decoder::IsSupported();
+  return !SupportedVP9Codecs().empty();
 }
 
 }  // namespace jni