Add ability to parse stable bwe experiment settings

Bug: webrtc:10126
Change-Id: If90aa2303b19d1ba9f9c53060e423ab1e6677ceb
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/149174
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Commit-Queue: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28960}
diff --git a/rtc_base/experiments/BUILD.gn b/rtc_base/experiments/BUILD.gn
index 927e8c7..830fd50 100644
--- a/rtc_base/experiments/BUILD.gn
+++ b/rtc_base/experiments/BUILD.gn
@@ -196,6 +196,20 @@
   ]
 }
 
+rtc_static_library("stable_target_rate_experiment") {
+  sources = [
+    "stable_target_rate_experiment.cc",
+    "stable_target_rate_experiment.h",
+  ]
+  deps = [
+    ":field_trial_parser",
+    ":rate_control_settings",
+    "../../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
@@ -212,6 +226,7 @@
       "quality_scaling_experiment_unittest.cc",
       "rate_control_settings_unittest.cc",
       "rtt_mult_experiment_unittest.cc",
+      "stable_target_rate_experiment_unittest.cc",
       "struct_parameters_parser_unittest.cc",
     ]
     deps = [
@@ -224,6 +239,7 @@
       ":quality_scaling_experiment",
       ":rate_control_settings",
       ":rtt_mult_experiment",
+      ":stable_target_rate_experiment",
       "..:gunit_helpers",
       "../:rtc_base_tests_utils",
       "../../api/video_codecs:video_codecs_api",
diff --git a/rtc_base/experiments/stable_target_rate_experiment.cc b/rtc_base/experiments/stable_target_rate_experiment.cc
new file mode 100644
index 0000000..28b5413
--- /dev/null
+++ b/rtc_base/experiments/stable_target_rate_experiment.cc
@@ -0,0 +1,72 @@
+/*
+ *  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/stable_target_rate_experiment.h"
+
+#include "api/transport/field_trial_based_config.h"
+#include "rtc_base/experiments/rate_control_settings.h"
+
+namespace webrtc {
+namespace {
+constexpr char kFieldTrialName[] = "WebRTC-StableTargetRate";
+}  // namespace
+
+StableTargetRateExperiment::StableTargetRateExperiment(
+    const WebRtcKeyValueConfig* const key_value_config,
+    absl::optional<double> default_video_hysteresis,
+    absl::optional<double> default_screenshare_hysteresis)
+    : enabled_("enabled", false),
+      video_hysteresis_factor_("video_hysteresis_factor",
+                               default_video_hysteresis),
+      screenshare_hysteresis_factor_("screenshare_hysteresis_factor",
+                                     default_screenshare_hysteresis) {
+  ParseFieldTrial(
+      {&enabled_, &video_hysteresis_factor_, &screenshare_hysteresis_factor_},
+      key_value_config->Lookup(kFieldTrialName));
+}
+
+StableTargetRateExperiment::StableTargetRateExperiment(
+    StableTargetRateExperiment&&) = default;
+
+StableTargetRateExperiment StableTargetRateExperiment::ParseFromFieldTrials() {
+  FieldTrialBasedConfig config;
+  return ParseFromKeyValueConfig(&config);
+}
+
+StableTargetRateExperiment StableTargetRateExperiment::ParseFromKeyValueConfig(
+    const WebRtcKeyValueConfig* const key_value_config) {
+  if (key_value_config->Lookup("WebRTC-VideoRateControl") != "") {
+    RateControlSettings rate_control =
+        RateControlSettings::ParseFromKeyValueConfig(key_value_config);
+    return StableTargetRateExperiment(key_value_config,
+                                      rate_control.GetSimulcastHysteresisFactor(
+                                          VideoCodecMode::kRealtimeVideo),
+                                      rate_control.GetSimulcastHysteresisFactor(
+                                          VideoCodecMode::kScreensharing));
+  }
+  return StableTargetRateExperiment(key_value_config, absl::nullopt,
+                                    absl::nullopt);
+}
+
+bool StableTargetRateExperiment::IsEnabled() const {
+  return enabled_.Get();
+}
+
+absl::optional<double> StableTargetRateExperiment::GetVideoHysteresisFactor()
+    const {
+  return video_hysteresis_factor_.GetOptional();
+}
+
+absl::optional<double>
+StableTargetRateExperiment::GetScreenshareHysteresisFactor() const {
+  return screenshare_hysteresis_factor_.GetOptional();
+}
+
+}  // namespace webrtc
diff --git a/rtc_base/experiments/stable_target_rate_experiment.h b/rtc_base/experiments/stable_target_rate_experiment.h
new file mode 100644
index 0000000..b56108d
--- /dev/null
+++ b/rtc_base/experiments/stable_target_rate_experiment.h
@@ -0,0 +1,43 @@
+/*
+ *  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.
+ */
+
+#ifndef RTC_BASE_EXPERIMENTS_STABLE_TARGET_RATE_EXPERIMENT_H_
+#define RTC_BASE_EXPERIMENTS_STABLE_TARGET_RATE_EXPERIMENT_H_
+
+#include "api/transport/webrtc_key_value_config.h"
+#include "rtc_base/experiments/field_trial_parser.h"
+
+namespace webrtc {
+
+class StableTargetRateExperiment {
+ public:
+  StableTargetRateExperiment(StableTargetRateExperiment&&);
+  static StableTargetRateExperiment ParseFromFieldTrials();
+  static StableTargetRateExperiment ParseFromKeyValueConfig(
+      const WebRtcKeyValueConfig* const key_value_config);
+
+  bool IsEnabled() const;
+  absl::optional<double> GetVideoHysteresisFactor() const;
+  absl::optional<double> GetScreenshareHysteresisFactor() const;
+
+ private:
+  explicit StableTargetRateExperiment(
+      const WebRtcKeyValueConfig* const key_value_config,
+      absl::optional<double> default_video_hysteresis,
+      absl::optional<double> default_screenshare_hysteresis);
+
+  FieldTrialParameter<bool> enabled_;
+  FieldTrialOptional<double> video_hysteresis_factor_;
+  FieldTrialOptional<double> screenshare_hysteresis_factor_;
+};
+
+}  // namespace webrtc
+
+#endif  // RTC_BASE_EXPERIMENTS_STABLE_TARGET_RATE_EXPERIMENT_H_
diff --git a/rtc_base/experiments/stable_target_rate_experiment_unittest.cc b/rtc_base/experiments/stable_target_rate_experiment_unittest.cc
new file mode 100644
index 0000000..86629f4
--- /dev/null
+++ b/rtc_base/experiments/stable_target_rate_experiment_unittest.cc
@@ -0,0 +1,80 @@
+/*
+ *  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/stable_target_rate_experiment.h"
+
+#include "test/field_trial.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+TEST(StableBweExperimentTest, Default) {
+  StableTargetRateExperiment config =
+      StableTargetRateExperiment::ParseFromFieldTrials();
+  EXPECT_FALSE(config.IsEnabled());
+  EXPECT_FALSE(config.GetVideoHysteresisFactor());
+  EXPECT_FALSE(config.GetScreenshareHysteresisFactor());
+}
+
+TEST(StableBweExperimentTest, EnabledNoHysteresis) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-StableTargetRate/enabled:true/");
+
+  StableTargetRateExperiment config =
+      StableTargetRateExperiment::ParseFromFieldTrials();
+  EXPECT_TRUE(config.IsEnabled());
+  EXPECT_FALSE(config.GetVideoHysteresisFactor());
+  EXPECT_FALSE(config.GetScreenshareHysteresisFactor());
+}
+
+TEST(StableBweExperimentTest, EnabledWithHysteresis) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-StableTargetRate/"
+      "enabled:true,"
+      "video_hysteresis_factor:1.1,"
+      "screenshare_hysteresis_factor:1.2/");
+
+  StableTargetRateExperiment config =
+      StableTargetRateExperiment::ParseFromFieldTrials();
+  EXPECT_TRUE(config.IsEnabled());
+  EXPECT_EQ(config.GetVideoHysteresisFactor(), 1.1);
+  EXPECT_EQ(config.GetScreenshareHysteresisFactor(), 1.2);
+}
+
+TEST(StableBweExperimentTest, OnNoHysteresisPropagatesVideoRateHystersis) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-StableTargetRate/enabled:true/"
+      "WebRTC-VideoRateControl/video_hysteresis:1.3,"
+      "screenshare_hysteresis:1.4/");
+
+  StableTargetRateExperiment config =
+      StableTargetRateExperiment::ParseFromFieldTrials();
+  EXPECT_TRUE(config.IsEnabled());
+  EXPECT_EQ(config.GetVideoHysteresisFactor(), 1.3);
+  EXPECT_EQ(config.GetScreenshareHysteresisFactor(), 1.4);
+}
+
+TEST(StableBweExperimentTest, HysteresisOverrideVideoRateHystersis) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-StableTargetRate/"
+      "enabled:true,"
+      "video_hysteresis_factor:1.1,"
+      "screenshare_hysteresis_factor:1.2/"
+      "WebRTC-VideoRateControl/video_hysteresis:1.3,"
+      "screenshare_hysteresis:1.4/");
+
+  StableTargetRateExperiment config =
+      StableTargetRateExperiment::ParseFromFieldTrials();
+  EXPECT_TRUE(config.IsEnabled());
+  EXPECT_EQ(config.GetVideoHysteresisFactor(), 1.1);
+  EXPECT_EQ(config.GetScreenshareHysteresisFactor(), 1.2);
+}
+
+}  // namespace webrtc