BalancedDegradationSettings: Add option to configure fps based on codec type.

Bug: none
Change-Id: I43b3d976b9400a0552fee80a6a65c215c71049ac
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/144543
Reviewed-by: Sergey Silkin <ssilkin@webrtc.org>
Commit-Queue: Åsa Persson <asapersson@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28503}
diff --git a/rtc_base/experiments/balanced_degradation_settings.cc b/rtc_base/experiments/balanced_degradation_settings.cc
index a8d7d4d..884a669 100644
--- a/rtc_base/experiments/balanced_degradation_settings.cc
+++ b/rtc_base/experiments/balanced_degradation_settings.cc
@@ -23,22 +23,43 @@
 constexpr int kMaxFps = 100;
 
 std::vector<BalancedDegradationSettings::Config> DefaultConfigs() {
-  return {{320 * 240, 7, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
-          {480 * 270, 10, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
-          {640 * 480, 15, {0, 0}, {0, 0}, {0, 0}, {0, 0}}};
+  return {{320 * 240, 7, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
+          {480 * 270, 10, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
+          {640 * 480, 15, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}};
 }
 
-bool IsValidThreshold(
-    const BalancedDegradationSettings::QpThreshold& threshold) {
-  if (threshold.GetLow().has_value() != threshold.GetHigh().has_value()) {
-    RTC_LOG(LS_WARNING) << "Neither or both values should be set.";
+bool IsValidConfig(
+    const BalancedDegradationSettings::CodecTypeSpecific& config) {
+  if (config.GetQpLow().has_value() != config.GetQpHigh().has_value()) {
+    RTC_LOG(LS_WARNING) << "Neither or both thresholds should be set.";
     return false;
   }
-  if (threshold.GetLow().has_value() && threshold.GetHigh().has_value() &&
-      threshold.GetLow().value() >= threshold.GetHigh().value()) {
+  if (config.GetQpLow().has_value() && config.GetQpHigh().has_value() &&
+      config.GetQpLow().value() >= config.GetQpHigh().value()) {
     RTC_LOG(LS_WARNING) << "Invalid threshold value, low >= high threshold.";
     return false;
   }
+  if (config.GetFps().has_value() && (config.GetFps().value() < kMinFps ||
+                                      config.GetFps().value() > kMaxFps)) {
+    RTC_LOG(LS_WARNING) << "Unsupported fps setting, value ignored.";
+    return false;
+  }
+  return true;
+}
+
+bool IsValid(const BalancedDegradationSettings::CodecTypeSpecific& config1,
+             const BalancedDegradationSettings::CodecTypeSpecific& config2) {
+  bool both_or_none_set = ((config1.qp_low > 0) == (config2.qp_low > 0) &&
+                           (config1.qp_high > 0) == (config2.qp_high > 0) &&
+                           (config1.fps > 0) == (config2.fps > 0));
+  if (!both_or_none_set) {
+    RTC_LOG(LS_WARNING) << "Invalid value, all/none should be set.";
+    return false;
+  }
+  if (config1.fps > 0 && config1.fps < config2.fps) {
+    RTC_LOG(LS_WARNING) << "Invalid fps/pixel value provided.";
+    return false;
+  }
   return true;
 }
 
@@ -59,21 +80,16 @@
       RTC_LOG(LS_WARNING) << "Invalid fps/pixel value provided.";
       return false;
     }
-    if (((configs[i].vp8.low > 0) != (configs[i - 1].vp8.low > 0)) ||
-        ((configs[i].vp9.low > 0) != (configs[i - 1].vp9.low > 0)) ||
-        ((configs[i].h264.low > 0) != (configs[i - 1].h264.low > 0)) ||
-        ((configs[i].generic.low > 0) != (configs[i - 1].generic.low > 0)) ||
-        ((configs[i].vp8.high > 0) != (configs[i - 1].vp8.high > 0)) ||
-        ((configs[i].vp9.high > 0) != (configs[i - 1].vp9.high > 0)) ||
-        ((configs[i].h264.high > 0) != (configs[i - 1].h264.high > 0)) ||
-        ((configs[i].generic.high > 0) != (configs[i - 1].generic.high > 0))) {
-      RTC_LOG(LS_WARNING) << "Invalid threshold value, all/none should be set.";
+    if (!IsValid(configs[i].vp8, configs[i - 1].vp8) ||
+        !IsValid(configs[i].vp9, configs[i - 1].vp9) ||
+        !IsValid(configs[i].h264, configs[i - 1].h264) ||
+        !IsValid(configs[i].generic, configs[i - 1].generic)) {
       return false;
     }
   }
   for (const auto& config : configs) {
-    if (!IsValidThreshold(config.vp8) || !IsValidThreshold(config.vp9) ||
-        !IsValidThreshold(config.h264) || !IsValidThreshold(config.generic)) {
+    if (!IsValidConfig(config.vp8) || !IsValidConfig(config.vp9) ||
+        !IsValidConfig(config.h264) || !IsValidConfig(config.generic)) {
       return false;
     }
   }
@@ -96,20 +112,20 @@
 
   switch (type) {
     case kVideoCodecVP8:
-      low = config.vp8.GetLow();
-      high = config.vp8.GetHigh();
+      low = config.vp8.GetQpLow();
+      high = config.vp8.GetQpHigh();
       break;
     case kVideoCodecVP9:
-      low = config.vp9.GetLow();
-      high = config.vp9.GetHigh();
+      low = config.vp9.GetQpLow();
+      high = config.vp9.GetQpHigh();
       break;
     case kVideoCodecH264:
-      low = config.h264.GetLow();
-      high = config.h264.GetHigh();
+      low = config.h264.GetQpLow();
+      high = config.h264.GetQpHigh();
       break;
     case kVideoCodecGeneric:
-      low = config.generic.GetLow();
-      high = config.generic.GetHigh();
+      low = config.generic.GetQpLow();
+      high = config.generic.GetQpHigh();
       break;
     default:
       break;
@@ -122,24 +138,58 @@
   }
   return absl::nullopt;
 }
+
+int GetFps(VideoCodecType type,
+           const absl::optional<BalancedDegradationSettings::Config>& config) {
+  if (!config.has_value()) {
+    return std::numeric_limits<int>::max();
+  }
+
+  absl::optional<int> fps;
+  switch (type) {
+    case kVideoCodecVP8:
+      fps = config->vp8.GetFps();
+      break;
+    case kVideoCodecVP9:
+      fps = config->vp9.GetFps();
+      break;
+    case kVideoCodecH264:
+      fps = config->h264.GetFps();
+      break;
+    case kVideoCodecGeneric:
+      fps = config->generic.GetFps();
+      break;
+    default:
+      break;
+  }
+
+  return fps.value_or(config->fps);
+}
 }  // namespace
 
-absl::optional<int> BalancedDegradationSettings::QpThreshold::GetLow() const {
-  return (low > 0) ? absl::optional<int>(low) : absl::nullopt;
+absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetQpLow()
+    const {
+  return (qp_low > 0) ? absl::optional<int>(qp_low) : absl::nullopt;
 }
 
-absl::optional<int> BalancedDegradationSettings::QpThreshold::GetHigh() const {
-  return (high > 0) ? absl::optional<int>(high) : absl::nullopt;
+absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetQpHigh()
+    const {
+  return (qp_high > 0) ? absl::optional<int>(qp_high) : absl::nullopt;
+}
+
+absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetFps()
+    const {
+  return (fps > 0) ? absl::optional<int>(fps) : absl::nullopt;
 }
 
 BalancedDegradationSettings::Config::Config() = default;
 
 BalancedDegradationSettings::Config::Config(int pixels,
                                             int fps,
-                                            QpThreshold vp8,
-                                            QpThreshold vp9,
-                                            QpThreshold h264,
-                                            QpThreshold generic)
+                                            CodecTypeSpecific vp8,
+                                            CodecTypeSpecific vp9,
+                                            CodecTypeSpecific h264,
+                                            CodecTypeSpecific generic)
     : pixels(pixels),
       fps(fps),
       vp8(vp8),
@@ -152,21 +202,27 @@
       {FieldTrialStructMember("pixels", [](Config* c) { return &c->pixels; }),
        FieldTrialStructMember("fps", [](Config* c) { return &c->fps; }),
        FieldTrialStructMember("vp8_qp_low",
-                              [](Config* c) { return &c->vp8.low; }),
+                              [](Config* c) { return &c->vp8.qp_low; }),
        FieldTrialStructMember("vp8_qp_high",
-                              [](Config* c) { return &c->vp8.high; }),
+                              [](Config* c) { return &c->vp8.qp_high; }),
+       FieldTrialStructMember("vp8_fps", [](Config* c) { return &c->vp8.fps; }),
        FieldTrialStructMember("vp9_qp_low",
-                              [](Config* c) { return &c->vp9.low; }),
+                              [](Config* c) { return &c->vp9.qp_low; }),
        FieldTrialStructMember("vp9_qp_high",
-                              [](Config* c) { return &c->vp9.high; }),
+                              [](Config* c) { return &c->vp9.qp_high; }),
+       FieldTrialStructMember("vp9_fps", [](Config* c) { return &c->vp9.fps; }),
        FieldTrialStructMember("h264_qp_low",
-                              [](Config* c) { return &c->h264.low; }),
+                              [](Config* c) { return &c->h264.qp_low; }),
        FieldTrialStructMember("h264_qp_high",
-                              [](Config* c) { return &c->h264.high; }),
+                              [](Config* c) { return &c->h264.qp_high; }),
+       FieldTrialStructMember("h264_fps",
+                              [](Config* c) { return &c->h264.fps; }),
        FieldTrialStructMember("generic_qp_low",
-                              [](Config* c) { return &c->generic.low; }),
+                              [](Config* c) { return &c->generic.qp_low; }),
        FieldTrialStructMember("generic_qp_high",
-                              [](Config* c) { return &c->generic.high; })},
+                              [](Config* c) { return &c->generic.qp_high; }),
+       FieldTrialStructMember("generic_fps",
+                              [](Config* c) { return &c->generic.fps; })},
       {});
 
   ParseFieldTrial({&configs}, field_trial::FindFullName(kFieldTrial));
@@ -182,20 +238,30 @@
   return configs_;
 }
 
-int BalancedDegradationSettings::MinFps(int pixels) const {
-  for (const auto& config : configs_) {
-    if (pixels <= config.pixels)
-      return config.fps;
-  }
-  return std::numeric_limits<int>::max();
+int BalancedDegradationSettings::MinFps(VideoCodecType type, int pixels) const {
+  return GetFps(type, GetMinFpsConfig(pixels));
 }
 
-int BalancedDegradationSettings::MaxFps(int pixels) const {
+absl::optional<BalancedDegradationSettings::Config>
+BalancedDegradationSettings::GetMinFpsConfig(int pixels) const {
+  for (const auto& config : configs_) {
+    if (pixels <= config.pixels)
+      return config;
+  }
+  return absl::nullopt;
+}
+
+int BalancedDegradationSettings::MaxFps(VideoCodecType type, int pixels) const {
+  return GetFps(type, GetMaxFpsConfig(pixels));
+}
+
+absl::optional<BalancedDegradationSettings::Config>
+BalancedDegradationSettings::GetMaxFpsConfig(int pixels) const {
   for (size_t i = 0; i < configs_.size() - 1; ++i) {
     if (pixels <= configs_[i].pixels)
-      return configs_[i + 1].fps;
+      return configs_[i + 1];
   }
-  return std::numeric_limits<int>::max();
+  return absl::nullopt;
 }
 
 absl::optional<VideoEncoder::QpThresholds>
diff --git a/rtc_base/experiments/balanced_degradation_settings.h b/rtc_base/experiments/balanced_degradation_settings.h
index ef4b587..448dea6 100644
--- a/rtc_base/experiments/balanced_degradation_settings.h
+++ b/rtc_base/experiments/balanced_degradation_settings.h
@@ -23,48 +23,51 @@
   BalancedDegradationSettings();
   ~BalancedDegradationSettings();
 
-  struct QpThreshold {
-    QpThreshold() {}
-    QpThreshold(int low, int high) : low(low), high(high) {}
+  struct CodecTypeSpecific {
+    CodecTypeSpecific() {}
+    CodecTypeSpecific(int qp_low, int qp_high, int fps)
+        : qp_low(qp_low), qp_high(qp_high), fps(fps) {}
 
-    bool operator==(const QpThreshold& o) const {
-      return low == o.low && high == o.high;
+    bool operator==(const CodecTypeSpecific& o) const {
+      return qp_low == o.qp_low && qp_high == o.qp_high && fps == o.fps;
     }
 
-    absl::optional<int> GetLow() const;
-    absl::optional<int> GetHigh() const;
-    int low = 0;
-    int high = 0;
+    absl::optional<int> GetQpLow() const;
+    absl::optional<int> GetQpHigh() const;
+    absl::optional<int> GetFps() const;
+    int qp_low = 0;
+    int qp_high = 0;
+    int fps = 0;
   };
 
   struct Config {
     Config();
     Config(int pixels,
            int fps,
-           QpThreshold vp8,
-           QpThreshold vp9,
-           QpThreshold h264,
-           QpThreshold generic);
+           CodecTypeSpecific vp8,
+           CodecTypeSpecific vp9,
+           CodecTypeSpecific h264,
+           CodecTypeSpecific generic);
 
     bool operator==(const Config& o) const {
       return pixels == o.pixels && fps == o.fps && vp8 == o.vp8 &&
              vp9 == o.vp9 && h264 == o.h264 && generic == o.generic;
     }
 
-    int pixels = 0;   // The video frame size.
-    int fps = 0;      // The framerate and thresholds to be used if the frame
-    QpThreshold vp8;  // size is less than or equal to |pixels|.
-    QpThreshold vp9;
-    QpThreshold h264;
-    QpThreshold generic;
+    int pixels = 0;         // The video frame size.
+    int fps = 0;            // The framerate and thresholds to be used if the
+    CodecTypeSpecific vp8;  // frame size is less than or equal to |pixels|.
+    CodecTypeSpecific vp9;
+    CodecTypeSpecific h264;
+    CodecTypeSpecific generic;
   };
 
   // Returns configurations from field trial on success (default on failure).
   std::vector<Config> GetConfigs() const;
 
   // Gets the min/max framerate from |configs_| based on |pixels|.
-  int MinFps(int pixels) const;
-  int MaxFps(int pixels) const;
+  int MinFps(VideoCodecType type, int pixels) const;
+  int MaxFps(VideoCodecType type, int pixels) const;
 
   // Gets QpThresholds for the codec |type| based on |pixels|.
   absl::optional<VideoEncoder::QpThresholds> GetQpThresholds(
@@ -72,6 +75,8 @@
       int pixels) const;
 
  private:
+  absl::optional<Config> GetMinFpsConfig(int pixels) const;
+  absl::optional<Config> GetMaxFpsConfig(int pixels) const;
   Config GetConfig(int pixels) const;
 
   std::vector<Config> configs_;
diff --git a/rtc_base/experiments/balanced_degradation_settings_unittest.cc b/rtc_base/experiments/balanced_degradation_settings_unittest.cc
index 8cbadac..c604b55 100644
--- a/rtc_base/experiments/balanced_degradation_settings_unittest.cc
+++ b/rtc_base/experiments/balanced_degradation_settings_unittest.cc
@@ -21,13 +21,15 @@
 
 void VerifyIsDefault(
     const std::vector<BalancedDegradationSettings::Config>& config) {
-  EXPECT_THAT(config, ::testing::ElementsAre(
-                          BalancedDegradationSettings::Config{
-                              320 * 240, 7, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
-                          BalancedDegradationSettings::Config{
-                              480 * 270, 10, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
-                          BalancedDegradationSettings::Config{
-                              640 * 480, 15, {0, 0}, {0, 0}, {0, 0}, {0, 0}}));
+  EXPECT_THAT(
+      config,
+      ::testing::ElementsAre(
+          BalancedDegradationSettings::Config{
+              320 * 240, 7, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
+          BalancedDegradationSettings::Config{
+              480 * 270, 10, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
+          BalancedDegradationSettings::Config{
+              640 * 480, 15, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}));
 }
 }  // namespace
 
@@ -50,11 +52,11 @@
   EXPECT_THAT(settings.GetConfigs(),
               ::testing::ElementsAre(
                   BalancedDegradationSettings::Config{
-                      11, 5, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
+                      11, 5, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
                   BalancedDegradationSettings::Config{
-                      22, 15, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
+                      22, 15, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
                   BalancedDegradationSettings::Config{
-                      33, 25, {0, 0}, {0, 0}, {0, 0}, {0, 0}}));
+                      33, 25, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}));
 }
 
 TEST(BalancedDegradationSettings, GetsDefaultConfigForZeroFpsValue) {
@@ -81,18 +83,75 @@
   VerifyIsDefault(settings.GetConfigs());
 }
 
+TEST(BalancedDegradationSettings, GetsConfigWithSpecificFps) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Video-BalancedDegradationSettings/"
+      "pixels:1000|2000|3000,fps:5|15|25,vp8_fps:7|8|9,vp9_fps:9|10|11,"
+      "h264_fps:11|12|13,generic_fps:13|14|15/");
+  BalancedDegradationSettings settings;
+  EXPECT_THAT(
+      settings.GetConfigs(),
+      ::testing::ElementsAre(
+          BalancedDegradationSettings::Config{
+              1000, 5, {0, 0, 7}, {0, 0, 9}, {0, 0, 11}, {0, 0, 13}},
+          BalancedDegradationSettings::Config{
+              2000, 15, {0, 0, 8}, {0, 0, 10}, {0, 0, 12}, {0, 0, 14}},
+          BalancedDegradationSettings::Config{
+              3000, 25, {0, 0, 9}, {0, 0, 11}, {0, 0, 13}, {0, 0, 15}}));
+}
+
+TEST(BalancedDegradationSettings, GetsDefaultConfigForZeroVp8FpsValue) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Video-BalancedDegradationSettings/"
+      "pixels:1000|2000|3000,fps:7|15|25,vp8_fps:0|15|25/");
+  BalancedDegradationSettings settings;
+  VerifyIsDefault(settings.GetConfigs());
+}
+
+TEST(BalancedDegradationSettings, GetsDefaultConfigForInvalidFpsValue) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Video-BalancedDegradationSettings/"
+      "pixels:1000|2000|3000,fps:7|15|25,vp8_fps:10|15|2000/");
+  BalancedDegradationSettings settings;
+  VerifyIsDefault(settings.GetConfigs());
+}
+
+TEST(BalancedDegradationSettings, GetsDefaultConfigIfVp8FramerateDecreases) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Video-BalancedDegradationSettings/"
+      "pixels:1000|2000|3000,fps:4|5|25,vp8_fps:5|4|25/");
+  BalancedDegradationSettings settings;
+  VerifyIsDefault(settings.GetConfigs());
+}
+
 TEST(BalancedDegradationSettings, GetsMinFps) {
   webrtc::test::ScopedFieldTrials field_trials(
       "WebRTC-Video-BalancedDegradationSettings/"
       "pixels:1000|2000|3000,fps:5|15|25/");
   BalancedDegradationSettings settings;
-  EXPECT_EQ(5, settings.MinFps(1));
-  EXPECT_EQ(5, settings.MinFps(1000));
-  EXPECT_EQ(15, settings.MinFps(1000 + 1));
-  EXPECT_EQ(15, settings.MinFps(2000));
-  EXPECT_EQ(25, settings.MinFps(2000 + 1));
-  EXPECT_EQ(25, settings.MinFps(3000));
-  EXPECT_EQ(std::numeric_limits<int>::max(), settings.MinFps(3000 + 1));
+  EXPECT_EQ(5, settings.MinFps(kVideoCodecVP8, 1));
+  EXPECT_EQ(5, settings.MinFps(kVideoCodecVP8, 1000));
+  EXPECT_EQ(15, settings.MinFps(kVideoCodecVP8, 1001));
+  EXPECT_EQ(15, settings.MinFps(kVideoCodecVP8, 2000));
+  EXPECT_EQ(25, settings.MinFps(kVideoCodecVP8, 2001));
+  EXPECT_EQ(25, settings.MinFps(kVideoCodecVP8, 3000));
+  EXPECT_EQ(std::numeric_limits<int>::max(),
+            settings.MinFps(kVideoCodecVP8, 3001));
+}
+
+TEST(BalancedDegradationSettings, GetsVp8MinFps) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Video-BalancedDegradationSettings/"
+      "pixels:1000|2000|3000,fps:5|15|25,vp8_fps:7|10|12/");
+  BalancedDegradationSettings settings;
+  EXPECT_EQ(7, settings.MinFps(kVideoCodecVP8, 1));
+  EXPECT_EQ(7, settings.MinFps(kVideoCodecVP8, 1000));
+  EXPECT_EQ(10, settings.MinFps(kVideoCodecVP8, 1001));
+  EXPECT_EQ(10, settings.MinFps(kVideoCodecVP8, 2000));
+  EXPECT_EQ(12, settings.MinFps(kVideoCodecVP8, 2001));
+  EXPECT_EQ(12, settings.MinFps(kVideoCodecVP8, 3000));
+  EXPECT_EQ(std::numeric_limits<int>::max(),
+            settings.MinFps(kVideoCodecVP8, 3001));
 }
 
 TEST(BalancedDegradationSettings, GetsMaxFps) {
@@ -100,11 +159,53 @@
       "WebRTC-Video-BalancedDegradationSettings/"
       "pixels:1000|2000|3000,fps:5|15|25/");
   BalancedDegradationSettings settings;
-  EXPECT_EQ(15, settings.MaxFps(1));
-  EXPECT_EQ(15, settings.MaxFps(1000));
-  EXPECT_EQ(25, settings.MaxFps(1000 + 1));
-  EXPECT_EQ(25, settings.MaxFps(2000));
-  EXPECT_EQ(std::numeric_limits<int>::max(), settings.MaxFps(2000 + 1));
+  EXPECT_EQ(15, settings.MaxFps(kVideoCodecVP8, 1));
+  EXPECT_EQ(15, settings.MaxFps(kVideoCodecVP8, 1000));
+  EXPECT_EQ(25, settings.MaxFps(kVideoCodecVP8, 1001));
+  EXPECT_EQ(25, settings.MaxFps(kVideoCodecVP8, 2000));
+  EXPECT_EQ(std::numeric_limits<int>::max(),
+            settings.MaxFps(kVideoCodecVP8, 2001));
+}
+
+TEST(BalancedDegradationSettings, GetsVp8MaxFps) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Video-BalancedDegradationSettings/"
+      "pixels:1000|2000|3000,fps:5|15|25,vp8_fps:7|10|12/");
+  BalancedDegradationSettings settings;
+  EXPECT_EQ(10, settings.MaxFps(kVideoCodecVP8, 1));
+  EXPECT_EQ(10, settings.MaxFps(kVideoCodecVP8, 1000));
+  EXPECT_EQ(12, settings.MaxFps(kVideoCodecVP8, 1001));
+  EXPECT_EQ(12, settings.MaxFps(kVideoCodecVP8, 2000));
+  EXPECT_EQ(std::numeric_limits<int>::max(),
+            settings.MaxFps(kVideoCodecVP8, 2001));
+}
+
+TEST(BalancedDegradationSettings, GetsVp9Fps) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Video-BalancedDegradationSettings/"
+      "pixels:1000|2000|3000,fps:5|15|25,vp9_fps:7|10|12/");
+  BalancedDegradationSettings settings;
+  EXPECT_EQ(7, settings.MinFps(kVideoCodecVP9, 1000));
+  EXPECT_EQ(10, settings.MaxFps(kVideoCodecVP9, 1000));
+}
+
+TEST(BalancedDegradationSettings, GetsH264Fps) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Video-BalancedDegradationSettings/"
+      "pixels:1000|2000|3000,fps:5|15|25,h264_fps:8|11|13/");
+  BalancedDegradationSettings settings;
+  EXPECT_EQ(11, settings.MinFps(kVideoCodecH264, 2000));
+  EXPECT_EQ(13, settings.MaxFps(kVideoCodecH264, 2000));
+}
+
+TEST(BalancedDegradationSettings, GetsGenericFps) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Video-BalancedDegradationSettings/"
+      "pixels:1000|2000|3000,fps:5|15|25,generic_fps:9|12|14/");
+  BalancedDegradationSettings settings;
+  EXPECT_EQ(14, settings.MinFps(kVideoCodecGeneric, 3000));
+  EXPECT_EQ(std::numeric_limits<int>::max(),
+            settings.MaxFps(kVideoCodecGeneric, 3000));
 }
 
 TEST(BalancedDegradationSettings, QpThresholdsNotSetByDefault) {
@@ -126,14 +227,15 @@
       "h264_qp_low:12|13|14,h264_qp_high:20|30|40,generic_qp_low:7|6|5,"
       "generic_qp_high:22|23|24/");
   BalancedDegradationSettings settings;
-  EXPECT_THAT(settings.GetConfigs(),
-              ::testing::ElementsAre(
-                  BalancedDegradationSettings::Config{
-                      1000, 5, {89, 90}, {27, 120}, {12, 20}, {7, 22}},
-                  BalancedDegradationSettings::Config{
-                      2000, 15, {90, 91}, {28, 130}, {13, 30}, {6, 23}},
-                  BalancedDegradationSettings::Config{
-                      3000, 25, {88, 92}, {29, 140}, {14, 40}, {5, 24}}));
+  EXPECT_THAT(
+      settings.GetConfigs(),
+      ::testing::ElementsAre(
+          BalancedDegradationSettings::Config{
+              1000, 5, {89, 90, 0}, {27, 120, 0}, {12, 20, 0}, {7, 22, 0}},
+          BalancedDegradationSettings::Config{
+              2000, 15, {90, 91, 0}, {28, 130, 0}, {13, 30, 0}, {6, 23, 0}},
+          BalancedDegradationSettings::Config{
+              3000, 25, {88, 92, 0}, {29, 140, 0}, {14, 40, 0}, {5, 24, 0}}));
 }
 
 TEST(BalancedDegradationSettings, GetsDefaultConfigIfOnlyHasLowThreshold) {
diff --git a/video/video_stream_encoder.cc b/video/video_stream_encoder.cc
index 005d49b..1361642 100644
--- a/video/video_stream_encoder.cc
+++ b/video/video_stream_encoder.cc
@@ -1741,7 +1741,8 @@
   switch (degradation_preference_) {
     case DegradationPreference::BALANCED: {
       // Try scale down framerate, if lower.
-      int fps = balanced_settings_.MinFps(last_frame_info_->pixel_count());
+      int fps = balanced_settings_.MinFps(encoder_config_.codec_type,
+                                          last_frame_info_->pixel_count());
       if (source_proxy_->RestrictFramerate(fps)) {
         GetAdaptCounter().IncrementFramerate(reason);
         break;
@@ -1817,7 +1818,8 @@
   switch (degradation_preference_) {
     case DegradationPreference::BALANCED: {
       // Try scale up framerate, if higher.
-      int fps = balanced_settings_.MaxFps(last_frame_info_->pixel_count());
+      int fps = balanced_settings_.MaxFps(encoder_config_.codec_type,
+                                          last_frame_info_->pixel_count());
       if (source_proxy_->IncreaseFramerate(fps)) {
         GetAdaptCounter().DecrementFramerate(reason, fps);
         // Reset framerate in case of fewer fps steps down than up.