Make keyframe generation/request intervals configurable through field trials.

Bug: webrtc:10427
Change-Id: I5e7182fc8932943adc3e5f147be51b0b5df93172
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/127882
Reviewed-by: Åsa Persson <asapersson@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Jonas Oreland <jonaso@webrtc.org>
Reviewed-by: Philip Eliasson <philipel@webrtc.org>
Commit-Queue: Rasmus Brandt <brandtr@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#27233}
diff --git a/rtc_base/experiments/BUILD.gn b/rtc_base/experiments/BUILD.gn
index afe174b..b8709dc 100644
--- a/rtc_base/experiments/BUILD.gn
+++ b/rtc_base/experiments/BUILD.gn
@@ -132,6 +132,19 @@
   ]
 }
 
+rtc_static_library("keyframe_interval_settings_experiment") {
+  sources = [
+    "keyframe_interval_settings.cc",
+    "keyframe_interval_settings.h",
+  ]
+  deps = [
+    ":field_trial_parser",
+    "../../api/transport:field_trial_based_config",
+    "../../api/transport:webrtc_key_value_config",
+    "//third_party/abseil-cpp/absl/types:optional",
+  ]
+}
+
 if (rtc_include_tests) {
   rtc_source_set("experiments_unittests") {
     testonly = true
@@ -140,6 +153,7 @@
       "cpu_speed_experiment_unittest.cc",
       "field_trial_parser_unittest.cc",
       "field_trial_units_unittest.cc",
+      "keyframe_interval_settings_unittest.cc",
       "normalize_simulcast_size_experiment_unittest.cc",
       "quality_scaling_experiment_unittest.cc",
       "rate_control_settings_unittest.cc",
@@ -148,6 +162,7 @@
     deps = [
       ":cpu_speed_experiment",
       ":field_trial_parser",
+      ":keyframe_interval_settings_experiment",
       ":normalize_simulcast_size_experiment",
       ":quality_scaling_experiment",
       ":rate_control_settings",
diff --git a/rtc_base/experiments/OWNERS b/rtc_base/experiments/OWNERS
index c31e020..5b34315 100644
--- a/rtc_base/experiments/OWNERS
+++ b/rtc_base/experiments/OWNERS
@@ -4,6 +4,7 @@
 per-file cpu_speed_experiment*=asapersson@webrtc.org
 per-file field_trial*=srte@webrtc.org
 per-file jitter_upper_bound_experiment*=sprang@webrtc.org
+per-file keyframe_interval_settings*=brandtr@webrtc.org
 per-file normalize_simulcast_size_experiment*=asapersson@webrtc.org
 per-file quality_scaling_experiment*=asapersson@webrtc.org
 per-file rtt_mult_experiment*=mhoro@webrtc.org
diff --git a/rtc_base/experiments/keyframe_interval_settings.cc b/rtc_base/experiments/keyframe_interval_settings.cc
new file mode 100644
index 0000000..2f19a1c
--- /dev/null
+++ b/rtc_base/experiments/keyframe_interval_settings.cc
@@ -0,0 +1,51 @@
+/*
+ *  Copyright (c) 2019 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 "rtc_base/experiments/keyframe_interval_settings.h"
+
+#include "api/transport/field_trial_based_config.h"
+
+namespace webrtc {
+
+namespace {
+
+constexpr char kFieldTrialName[] = "WebRTC-KeyframeInterval";
+
+}  // namespace
+
+KeyframeIntervalSettings::KeyframeIntervalSettings(
+    const WebRtcKeyValueConfig* const key_value_config)
+    : min_keyframe_send_interval_ms_("min_keyframe_send_interval_ms"),
+      max_wait_for_keyframe_ms_("max_wait_for_keyframe_ms"),
+      max_wait_for_frame_ms_("max_wait_for_frame_ms") {
+  ParseFieldTrial({&min_keyframe_send_interval_ms_, &max_wait_for_keyframe_ms_,
+                   &max_wait_for_frame_ms_},
+                  key_value_config->Lookup(kFieldTrialName));
+}
+
+KeyframeIntervalSettings KeyframeIntervalSettings::ParseFromFieldTrials() {
+  FieldTrialBasedConfig field_trial_config;
+  return KeyframeIntervalSettings(&field_trial_config);
+}
+
+absl::optional<int> KeyframeIntervalSettings::MinKeyframeSendIntervalMs()
+    const {
+  return min_keyframe_send_interval_ms_.GetOptional();
+}
+
+absl::optional<int> KeyframeIntervalSettings::MaxWaitForKeyframeMs() const {
+  return max_wait_for_keyframe_ms_.GetOptional();
+}
+
+absl::optional<int> KeyframeIntervalSettings::MaxWaitForFrameMs() const {
+  return max_wait_for_frame_ms_.GetOptional();
+}
+
+}  // namespace webrtc
diff --git a/rtc_base/experiments/keyframe_interval_settings.h b/rtc_base/experiments/keyframe_interval_settings.h
new file mode 100644
index 0000000..7c8d6d3
--- /dev/null
+++ b/rtc_base/experiments/keyframe_interval_settings.h
@@ -0,0 +1,48 @@
+/*
+ *  Copyright (c) 2019 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 RTC_BASE_EXPERIMENTS_KEYFRAME_INTERVAL_SETTINGS_H_
+#define RTC_BASE_EXPERIMENTS_KEYFRAME_INTERVAL_SETTINGS_H_
+
+#include "absl/types/optional.h"
+#include "api/transport/webrtc_key_value_config.h"
+#include "rtc_base/experiments/field_trial_parser.h"
+
+namespace webrtc {
+
+class KeyframeIntervalSettings final {
+ public:
+  static KeyframeIntervalSettings ParseFromFieldTrials();
+
+  // Sender side.
+  // The encoded keyframe send rate is <= 1/MinKeyframeSendIntervalMs().
+  absl::optional<int> MinKeyframeSendIntervalMs() const;
+
+  // Receiver side.
+  // The keyframe request send rate is
+  //   - when we have not received a key frame at all:
+  //       <= 1/MaxWaitForKeyframeMs()
+  //   - when we have not received a frame recently:
+  //       <= 1/MaxWaitForFrameMs()
+  absl::optional<int> MaxWaitForKeyframeMs() const;
+  absl::optional<int> MaxWaitForFrameMs() const;
+
+ private:
+  explicit KeyframeIntervalSettings(
+      const WebRtcKeyValueConfig* key_value_config);
+
+  FieldTrialOptional<int> min_keyframe_send_interval_ms_;
+  FieldTrialOptional<int> max_wait_for_keyframe_ms_;
+  FieldTrialOptional<int> max_wait_for_frame_ms_;
+};
+
+}  // namespace webrtc
+
+#endif  // RTC_BASE_EXPERIMENTS_KEYFRAME_INTERVAL_SETTINGS_H_
diff --git a/rtc_base/experiments/keyframe_interval_settings_unittest.cc b/rtc_base/experiments/keyframe_interval_settings_unittest.cc
new file mode 100644
index 0000000..5457dc4
--- /dev/null
+++ b/rtc_base/experiments/keyframe_interval_settings_unittest.cc
@@ -0,0 +1,86 @@
+/*
+ *  Copyright 2019 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 "rtc_base/experiments/keyframe_interval_settings.h"
+#include "test/field_trial.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+
+TEST(KeyframeIntervalSettingsTest, ParsesMinKeyframeSendIntervalMs) {
+  EXPECT_FALSE(KeyframeIntervalSettings::ParseFromFieldTrials()
+                   .MinKeyframeSendIntervalMs());
+
+  test::ScopedFieldTrials field_trials(
+      "WebRTC-KeyframeInterval/min_keyframe_send_interval_ms:100/");
+  EXPECT_EQ(KeyframeIntervalSettings::ParseFromFieldTrials()
+                .MinKeyframeSendIntervalMs(),
+            100);
+}
+
+TEST(KeyframeIntervalSettingsTest, ParsesMaxWaitForKeyframeMs) {
+  EXPECT_FALSE(
+      KeyframeIntervalSettings::ParseFromFieldTrials().MaxWaitForKeyframeMs());
+
+  test::ScopedFieldTrials field_trials(
+      "WebRTC-KeyframeInterval/max_wait_for_keyframe_ms:100/");
+  EXPECT_EQ(
+      KeyframeIntervalSettings::ParseFromFieldTrials().MaxWaitForKeyframeMs(),
+      100);
+}
+
+TEST(KeyframeIntervalSettingsTest, ParsesMaxWaitForFrameMs) {
+  EXPECT_FALSE(
+      KeyframeIntervalSettings::ParseFromFieldTrials().MaxWaitForFrameMs());
+
+  test::ScopedFieldTrials field_trials(
+      "WebRTC-KeyframeInterval/max_wait_for_frame_ms:100/");
+  EXPECT_EQ(
+      KeyframeIntervalSettings::ParseFromFieldTrials().MaxWaitForFrameMs(),
+      100);
+}
+
+TEST(KeyframeIntervalSettingsTest, ParsesAllValues) {
+  test::ScopedFieldTrials field_trials(
+      "WebRTC-KeyframeInterval/"
+      "min_keyframe_send_interval_ms:100,"
+      "max_wait_for_keyframe_ms:101,"
+      "max_wait_for_frame_ms:102/");
+  EXPECT_EQ(KeyframeIntervalSettings::ParseFromFieldTrials()
+                .MinKeyframeSendIntervalMs(),
+            100);
+  EXPECT_EQ(
+      KeyframeIntervalSettings::ParseFromFieldTrials().MaxWaitForKeyframeMs(),
+      101);
+  EXPECT_EQ(
+      KeyframeIntervalSettings::ParseFromFieldTrials().MaxWaitForFrameMs(),
+      102);
+}
+
+TEST(KeyframeIntervalSettingsTest, DoesNotParseAllValuesWhenIncorrectlySet) {
+  EXPECT_FALSE(
+      KeyframeIntervalSettings::ParseFromFieldTrials().MaxWaitForFrameMs());
+
+  test::ScopedFieldTrials field_trials(
+      "WebRTC-KeyframeInterval/"
+      "min_keyframe_send_interval_ms:a,"
+      "max_wait_for_keyframe_ms:b,"
+      "max_wait_for_frame_ms:c/");
+  EXPECT_FALSE(KeyframeIntervalSettings::ParseFromFieldTrials()
+                   .MinKeyframeSendIntervalMs());
+  EXPECT_FALSE(
+      KeyframeIntervalSettings::ParseFromFieldTrials().MaxWaitForKeyframeMs());
+  EXPECT_FALSE(
+      KeyframeIntervalSettings::ParseFromFieldTrials().MaxWaitForFrameMs());
+}
+
+}  // namespace
+}  // namespace webrtc
diff --git a/video/BUILD.gn b/video/BUILD.gn
index 0e02401..8f89e22 100644
--- a/video/BUILD.gn
+++ b/video/BUILD.gn
@@ -99,6 +99,7 @@
     "../rtc_base:stringutils",
     "../rtc_base:weak_ptr",
     "../rtc_base/experiments:alr_experiment",
+    "../rtc_base/experiments:keyframe_interval_settings_experiment",
     "../rtc_base/experiments:quality_scaling_experiment",
     "../rtc_base/experiments:rate_control_settings",
     "../rtc_base/system:fallthrough",
diff --git a/video/encoder_key_frame_callback.cc b/video/encoder_key_frame_callback.cc
index 447a3af..3116221 100644
--- a/video/encoder_key_frame_callback.cc
+++ b/video/encoder_key_frame_callback.cc
@@ -10,12 +10,16 @@
 
 #include "video/encoder_key_frame_callback.h"
 
+#include "absl/types/optional.h"
 #include "rtc_base/checks.h"
-
-static const int kMinKeyFrameRequestIntervalMs = 300;
+#include "rtc_base/experiments/keyframe_interval_settings.h"
 
 namespace webrtc {
 
+namespace {
+constexpr int kMinKeyframeSendIntervalMs = 300;
+}  // namespace
+
 EncoderKeyFrameCallback::EncoderKeyFrameCallback(
     Clock* clock,
     const std::vector<uint32_t>& ssrcs,
@@ -23,7 +27,11 @@
     : clock_(clock),
       ssrcs_(ssrcs),
       video_stream_encoder_(encoder),
-      time_last_intra_request_ms_(-1) {
+      time_last_intra_request_ms_(-1),
+      min_keyframe_send_interval_ms_(
+          KeyframeIntervalSettings::ParseFromFieldTrials()
+              .MinKeyframeSendIntervalMs()
+              .value_or(kMinKeyframeSendIntervalMs)) {
   RTC_DCHECK(!ssrcs.empty());
 }
 
@@ -41,7 +49,7 @@
   {
     int64_t now_ms = clock_->TimeInMilliseconds();
     rtc::CritScope lock(&crit_);
-    if (time_last_intra_request_ms_ + kMinKeyFrameRequestIntervalMs > now_ms) {
+    if (time_last_intra_request_ms_ + min_keyframe_send_interval_ms_ > now_ms) {
       return;
     }
     time_last_intra_request_ms_ = now_ms;
diff --git a/video/encoder_key_frame_callback.h b/video/encoder_key_frame_callback.h
index 4e0ac56..9ed3c578 100644
--- a/video/encoder_key_frame_callback.h
+++ b/video/encoder_key_frame_callback.h
@@ -46,6 +46,8 @@
 
   rtc::CriticalSection crit_;
   int64_t time_last_intra_request_ms_ RTC_GUARDED_BY(crit_);
+
+  const int min_keyframe_send_interval_ms_;
 };
 
 }  // namespace webrtc
diff --git a/video/video_receive_stream.cc b/video/video_receive_stream.cc
index 790b7b3..bea06c0 100644
--- a/video/video_receive_stream.cc
+++ b/video/video_receive_stream.cc
@@ -39,6 +39,7 @@
 #include "modules/video_coding/timing.h"
 #include "modules/video_coding/utility/vp8_header_parser.h"
 #include "rtc_base/checks.h"
+#include "rtc_base/experiments/keyframe_interval_settings.h"
 #include "rtc_base/location.h"
 #include "rtc_base/logging.h"
 #include "rtc_base/platform_file.h"
@@ -58,6 +59,9 @@
 constexpr int kMinBaseMinimumDelayMs = 0;
 constexpr int kMaxBaseMinimumDelayMs = 10000;
 
+constexpr int kMaxWaitForKeyFrameMs = 200;
+constexpr int kMaxWaitForFrameMs = 3000;
+
 VideoCodec CreateDecoderVideoCodec(const VideoReceiveStream::Decoder& decoder) {
   VideoCodec codec;
   memset(&codec, 0, sizeof(codec));
@@ -205,7 +209,13 @@
                                  this,  // KeyFrameRequestSender
                                  this,  // OnCompleteFrameCallback
                                  config_.frame_decryptor),
-      rtp_stream_sync_(this) {
+      rtp_stream_sync_(this),
+      max_wait_for_keyframe_ms_(KeyframeIntervalSettings::ParseFromFieldTrials()
+                                    .MaxWaitForKeyframeMs()
+                                    .value_or(kMaxWaitForKeyFrameMs)),
+      max_wait_for_frame_ms_(KeyframeIntervalSettings::ParseFromFieldTrials()
+                                 .MaxWaitForFrameMs()
+                                 .value_or(kMaxWaitForFrameMs)) {
   RTC_LOG(LS_INFO) << "VideoReceiveStream: " << config_.ToString();
 
   RTC_DCHECK(config_.renderer);
@@ -565,10 +575,9 @@
 
 bool VideoReceiveStream::Decode() {
   TRACE_EVENT0("webrtc", "VideoReceiveStream::Decode");
-  static const int kMaxWaitForFrameMs = 3000;
-  static const int kMaxWaitForKeyFrameMs = 200;
 
-  int wait_ms = keyframe_required_ ? kMaxWaitForKeyFrameMs : kMaxWaitForFrameMs;
+  const int wait_ms =
+      keyframe_required_ ? max_wait_for_keyframe_ms_ : max_wait_for_frame_ms_;
   std::unique_ptr<video_coding::EncodedFrame> frame;
   // TODO(philipel): Call NextFrame with |keyframe_required| argument when
   //                 downstream project has been fixed.
@@ -602,7 +611,8 @@
       if (decode_result == WEBRTC_VIDEO_CODEC_OK_REQUEST_KEYFRAME)
         RequestKeyFrame();
     } else if (!frame_decoded_ || !keyframe_required_ ||
-               (last_keyframe_request_ms_ + kMaxWaitForKeyFrameMs < now_ms)) {
+               (last_keyframe_request_ms_ + max_wait_for_keyframe_ms_ <
+                now_ms)) {
       keyframe_required_ = true;
       // TODO(philipel): Remove this keyframe request when downstream project
       //                 has been fixed.
@@ -627,7 +637,7 @@
     // we assume a keyframe is currently being received.
     bool receiving_keyframe =
         last_keyframe_packet_ms &&
-        now_ms - *last_keyframe_packet_ms < kMaxWaitForKeyFrameMs;
+        now_ms - *last_keyframe_packet_ms < max_wait_for_keyframe_ms_;
 
     if (stream_is_active && !receiving_keyframe &&
         (!config_.crypto_options.sframe.require_frame_encryption ||
diff --git a/video/video_receive_stream.h b/video/video_receive_stream.h
index 3730505..162ef8c 100644
--- a/video/video_receive_stream.h
+++ b/video/video_receive_stream.h
@@ -184,6 +184,10 @@
   int64_t last_keyframe_request_ms_ = 0;
   int64_t last_complete_frame_time_ms_ = 0;
 
+  // Keyframe request intervals are configurable through field trials.
+  const int max_wait_for_keyframe_ms_;
+  const int max_wait_for_frame_ms_;
+
   rtc::CriticalSection playout_delay_lock_;
 
   // All of them tries to change current min_playout_delay on |timing_| but