Prepare iOS H264 HW encoder for High Profile

BUG=webrtc:6337

Review-Url: https://codereview.webrtc.org/2484493002
Cr-Commit-Position: refs/heads/master@{#15091}
diff --git a/webrtc/sdk/objc/Framework/Classes/h264_video_toolbox_encoder.h b/webrtc/sdk/objc/Framework/Classes/h264_video_toolbox_encoder.h
index 3acdf64..2413f77 100644
--- a/webrtc/sdk/objc/Framework/Classes/h264_video_toolbox_encoder.h
+++ b/webrtc/sdk/objc/Framework/Classes/h264_video_toolbox_encoder.h
@@ -16,6 +16,7 @@
 #include "webrtc/common_video/h264/h264_bitstream_parser.h"
 #include "webrtc/common_video/include/bitrate_adjuster.h"
 #include "webrtc/common_video/rotation.h"
+#include "webrtc/media/base/codec.h"
 #include "webrtc/modules/video_coding/codecs/h264/include/h264.h"
 #include "webrtc/modules/video_coding/utility/quality_scaler.h"
 
@@ -31,7 +32,7 @@
 
 class H264VideoToolboxEncoder : public H264Encoder {
  public:
-  H264VideoToolboxEncoder();
+  explicit H264VideoToolboxEncoder(const cricket::VideoCodec& codec);
 
   ~H264VideoToolboxEncoder() override;
 
@@ -82,6 +83,7 @@
   uint32_t encoder_bitrate_bps_;
   int32_t width_;
   int32_t height_;
+  const CFStringRef profile_;
 
   rtc::CriticalSection quality_scaler_crit_;
   QualityScaler quality_scaler_ GUARDED_BY(quality_scaler_crit_);
diff --git a/webrtc/sdk/objc/Framework/Classes/h264_video_toolbox_encoder.mm b/webrtc/sdk/objc/Framework/Classes/h264_video_toolbox_encoder.mm
index d3ce820..6393959 100644
--- a/webrtc/sdk/objc/Framework/Classes/h264_video_toolbox_encoder.mm
+++ b/webrtc/sdk/objc/Framework/Classes/h264_video_toolbox_encoder.mm
@@ -22,6 +22,7 @@
 #include "libyuv/convert_from.h"
 #include "webrtc/base/checks.h"
 #include "webrtc/base/logging.h"
+#include "webrtc/common_video/h264/profile_level_id.h"
 #include "webrtc/common_video/include/corevideo_frame_buffer.h"
 #include "webrtc/sdk/objc/Framework/Classes/h264_video_toolbox_nalu.h"
 #include "webrtc/system_wrappers/include/clock.h"
@@ -224,6 +225,112 @@
       encode_params->rotation);
 }
 
+// Extract VideoToolbox profile out of the cricket::VideoCodec. If there is no
+// specific VideoToolbox profile for the specified level, AutoLevel will be
+// returned. The user must initialize the encoder with a resolution and
+// framerate conforming to the selected H264 level regardless.
+CFStringRef ExtractProfile(const cricket::VideoCodec& codec) {
+  const rtc::Optional<webrtc::H264::ProfileLevelId> profile_level_id =
+      webrtc::H264::ParseSdpProfileLevelId(codec.params);
+  RTC_DCHECK(profile_level_id);
+  switch (profile_level_id->profile) {
+    case webrtc::H264::kProfileConstrainedBaseline:
+    case webrtc::H264::kProfileBaseline:
+      switch (profile_level_id->level) {
+        case webrtc::H264::kLevel3:
+          return kVTProfileLevel_H264_Baseline_3_0;
+        case webrtc::H264::kLevel3_1:
+          return kVTProfileLevel_H264_Baseline_3_1;
+        case webrtc::H264::kLevel3_2:
+          return kVTProfileLevel_H264_Baseline_3_2;
+        case webrtc::H264::kLevel4:
+          return kVTProfileLevel_H264_Baseline_4_0;
+        case webrtc::H264::kLevel4_1:
+          return kVTProfileLevel_H264_Baseline_4_1;
+        case webrtc::H264::kLevel4_2:
+          return kVTProfileLevel_H264_Baseline_4_2;
+        case webrtc::H264::kLevel5:
+          return kVTProfileLevel_H264_Baseline_5_0;
+        case webrtc::H264::kLevel5_1:
+          return kVTProfileLevel_H264_Baseline_5_1;
+        case webrtc::H264::kLevel5_2:
+          return kVTProfileLevel_H264_Baseline_5_2;
+        case webrtc::H264::kLevel1:
+        case webrtc::H264::kLevel1_b:
+        case webrtc::H264::kLevel1_1:
+        case webrtc::H264::kLevel1_2:
+        case webrtc::H264::kLevel1_3:
+        case webrtc::H264::kLevel2:
+        case webrtc::H264::kLevel2_1:
+        case webrtc::H264::kLevel2_2:
+          return kVTProfileLevel_H264_Baseline_AutoLevel;
+      }
+
+    case webrtc::H264::kProfileMain:
+      switch (profile_level_id->level) {
+        case webrtc::H264::kLevel3:
+          return kVTProfileLevel_H264_Main_3_0;
+        case webrtc::H264::kLevel3_1:
+          return kVTProfileLevel_H264_Main_3_1;
+        case webrtc::H264::kLevel3_2:
+          return kVTProfileLevel_H264_Main_3_2;
+        case webrtc::H264::kLevel4:
+          return kVTProfileLevel_H264_Main_4_0;
+        case webrtc::H264::kLevel4_1:
+          return kVTProfileLevel_H264_Main_4_1;
+        case webrtc::H264::kLevel4_2:
+          return kVTProfileLevel_H264_Main_4_2;
+        case webrtc::H264::kLevel5:
+          return kVTProfileLevel_H264_Main_5_0;
+        case webrtc::H264::kLevel5_1:
+          return kVTProfileLevel_H264_Main_5_1;
+        case webrtc::H264::kLevel5_2:
+          return kVTProfileLevel_H264_Main_5_2;
+        case webrtc::H264::kLevel1:
+        case webrtc::H264::kLevel1_b:
+        case webrtc::H264::kLevel1_1:
+        case webrtc::H264::kLevel1_2:
+        case webrtc::H264::kLevel1_3:
+        case webrtc::H264::kLevel2:
+        case webrtc::H264::kLevel2_1:
+        case webrtc::H264::kLevel2_2:
+          return kVTProfileLevel_H264_Main_AutoLevel;
+      }
+
+    case webrtc::H264::kProfileConstrainedHigh:
+    case webrtc::H264::kProfileHigh:
+      switch (profile_level_id->level) {
+        case webrtc::H264::kLevel3:
+          return kVTProfileLevel_H264_High_3_0;
+        case webrtc::H264::kLevel3_1:
+          return kVTProfileLevel_H264_High_3_1;
+        case webrtc::H264::kLevel3_2:
+          return kVTProfileLevel_H264_High_3_2;
+        case webrtc::H264::kLevel4:
+          return kVTProfileLevel_H264_High_4_0;
+        case webrtc::H264::kLevel4_1:
+          return kVTProfileLevel_H264_High_4_1;
+        case webrtc::H264::kLevel4_2:
+          return kVTProfileLevel_H264_High_4_2;
+        case webrtc::H264::kLevel5:
+          return kVTProfileLevel_H264_High_5_0;
+        case webrtc::H264::kLevel5_1:
+          return kVTProfileLevel_H264_High_5_1;
+        case webrtc::H264::kLevel5_2:
+          return kVTProfileLevel_H264_High_5_2;
+        case webrtc::H264::kLevel1:
+        case webrtc::H264::kLevel1_b:
+        case webrtc::H264::kLevel1_1:
+        case webrtc::H264::kLevel1_2:
+        case webrtc::H264::kLevel1_3:
+        case webrtc::H264::kLevel2:
+        case webrtc::H264::kLevel2_1:
+        case webrtc::H264::kLevel2_2:
+          return kVTProfileLevel_H264_High_AutoLevel;
+      }
+  }
+}
+
 }  // namespace internal
 
 namespace webrtc {
@@ -235,10 +342,13 @@
 // drastically reduced bitrate, so we want to avoid that. In steady state
 // conditions, 0.95 seems to give us better overall bitrate over long periods
 // of time.
-H264VideoToolboxEncoder::H264VideoToolboxEncoder()
+H264VideoToolboxEncoder::H264VideoToolboxEncoder(
+    const cricket::VideoCodec& codec)
     : callback_(nullptr),
       compression_session_(nullptr),
-      bitrate_adjuster_(Clock::GetRealTimeClock(), .5, .95) {
+      bitrate_adjuster_(Clock::GetRealTimeClock(), .5, .95),
+      profile_(internal::ExtractProfile(codec)) {
+  LOG(LS_INFO) << "Using profile " << internal::CFStringToString(profile_);
 }
 
 H264VideoToolboxEncoder::~H264VideoToolboxEncoder() {
@@ -496,7 +606,7 @@
                                  kVTCompressionPropertyKey_RealTime, true);
   internal::SetVTSessionProperty(compression_session_,
                                  kVTCompressionPropertyKey_ProfileLevel,
-                                 kVTProfileLevel_H264_Baseline_AutoLevel);
+                                 profile_);
   internal::SetVTSessionProperty(compression_session_,
                                  kVTCompressionPropertyKey_AllowFrameReordering,
                                  false);
diff --git a/webrtc/sdk/objc/Framework/Classes/videotoolboxvideocodecfactory.cc b/webrtc/sdk/objc/Framework/Classes/videotoolboxvideocodecfactory.cc
index 0772530..4bf7a4d 100644
--- a/webrtc/sdk/objc/Framework/Classes/videotoolboxvideocodecfactory.cc
+++ b/webrtc/sdk/objc/Framework/Classes/videotoolboxvideocodecfactory.cc
@@ -10,6 +10,7 @@
 #include "webrtc/sdk/objc/Framework/Classes/videotoolboxvideocodecfactory.h"
 
 #include "webrtc/base/logging.h"
+#include "webrtc/common_video/h264/profile_level_id.h"
 #include "webrtc/media/base/codec.h"
 #if defined(WEBRTC_IOS)
 #include "webrtc/sdk/objc/Framework/Classes/h264_video_toolbox_encoder.h"
@@ -38,7 +39,19 @@
 VideoToolboxVideoEncoderFactory::VideoToolboxVideoEncoderFactory() {
 // Hardware H264 encoding only supported on iOS for now.
 #if defined(WEBRTC_IOS)
-  supported_codecs_.push_back(cricket::VideoCodec(cricket::kH264CodecName));
+  // TODO(magjed): Push Constrained High profile as well when negotiation is
+  // ready, http://crbug/webrtc/6337.
+  cricket::VideoCodec constrained_baseline(cricket::kH264CodecName);
+  // TODO(magjed): Enumerate actual level instead of using hardcoded level 3.1.
+  // Level 3.1 is 1280x720@30fps which is enough for now.
+  const H264::ProfileLevelId constrained_baseline_profile(
+      H264::kProfileConstrainedBaseline, H264::kLevel3_1);
+  constrained_baseline.SetParam(
+      cricket::kH264FmtpProfileLevelId,
+      *H264::ProfileLevelIdToString(constrained_baseline_profile));
+  constrained_baseline.SetParam(cricket::kH264FmtpLevelAsymmetryAllowed, "1");
+  constrained_baseline.SetParam(cricket::kH264FmtpPacketizationMode, "1");
+  supported_codecs_.push_back(constrained_baseline);
 #endif
 }
 
@@ -49,7 +62,7 @@
 #if defined(WEBRTC_IOS)
   if (FindMatchingCodec(supported_codecs_, codec)) {
     LOG(LS_INFO) << "Creating HW encoder for " << codec.name;
-    return new H264VideoToolboxEncoder();
+    return new H264VideoToolboxEncoder(codec);
   }
 #endif
   LOG(LS_INFO) << "No HW encoder found for codec " << codec.name;