blob: 40c9e87a69a8b2ae3acf23339d433678ff0dd410 [file] [log] [blame]
Åsa Perssonf3d828e2019-05-06 12:22:49 +02001/*
2 * Copyright 2019 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "rtc_base/experiments/balanced_degradation_settings.h"
Jonas Olssona4d87372019-07-05 19:08:33 +020012
Åsa Perssonf3d828e2019-05-06 12:22:49 +020013#include <limits>
14
15#include "rtc_base/experiments/field_trial_list.h"
16#include "rtc_base/experiments/field_trial_parser.h"
17#include "rtc_base/logging.h"
18#include "system_wrappers/include/field_trial.h"
19
20namespace webrtc {
21namespace {
22constexpr char kFieldTrial[] = "WebRTC-Video-BalancedDegradationSettings";
23constexpr int kMinFps = 1;
Åsa Persson0c38a862019-08-14 10:10:12 +020024constexpr int kMaxFps = 100; // 100 means unlimited fps.
Åsa Perssonf3d828e2019-05-06 12:22:49 +020025
26std::vector<BalancedDegradationSettings::Config> DefaultConfigs() {
Åsa Perssonf5e5d252019-08-16 17:24:59 +020027 return {{320 * 240,
28 7,
29 0,
30 BalancedDegradationSettings::kNoFpsDiff,
31 {0, 0, 0},
32 {0, 0, 0},
33 {0, 0, 0},
34 {0, 0, 0}},
35 {480 * 270,
36 10,
37 0,
38 BalancedDegradationSettings::kNoFpsDiff,
39 {0, 0, 0},
40 {0, 0, 0},
41 {0, 0, 0},
42 {0, 0, 0}},
43 {640 * 480,
44 15,
45 0,
46 BalancedDegradationSettings::kNoFpsDiff,
47 {0, 0, 0},
48 {0, 0, 0},
49 {0, 0, 0},
50 {0, 0, 0}}};
Åsa Persson12314192019-06-20 15:45:07 +020051}
52
Åsa Persson48284b82019-07-08 10:01:12 +020053bool IsValidConfig(
54 const BalancedDegradationSettings::CodecTypeSpecific& config) {
55 if (config.GetQpLow().has_value() != config.GetQpHigh().has_value()) {
56 RTC_LOG(LS_WARNING) << "Neither or both thresholds should be set.";
Åsa Persson12314192019-06-20 15:45:07 +020057 return false;
58 }
Åsa Persson48284b82019-07-08 10:01:12 +020059 if (config.GetQpLow().has_value() && config.GetQpHigh().has_value() &&
60 config.GetQpLow().value() >= config.GetQpHigh().value()) {
Åsa Persson12314192019-06-20 15:45:07 +020061 RTC_LOG(LS_WARNING) << "Invalid threshold value, low >= high threshold.";
62 return false;
63 }
Åsa Persson48284b82019-07-08 10:01:12 +020064 if (config.GetFps().has_value() && (config.GetFps().value() < kMinFps ||
65 config.GetFps().value() > kMaxFps)) {
66 RTC_LOG(LS_WARNING) << "Unsupported fps setting, value ignored.";
67 return false;
68 }
69 return true;
70}
71
72bool IsValid(const BalancedDegradationSettings::CodecTypeSpecific& config1,
73 const BalancedDegradationSettings::CodecTypeSpecific& config2) {
74 bool both_or_none_set = ((config1.qp_low > 0) == (config2.qp_low > 0) &&
75 (config1.qp_high > 0) == (config2.qp_high > 0) &&
76 (config1.fps > 0) == (config2.fps > 0));
77 if (!both_or_none_set) {
78 RTC_LOG(LS_WARNING) << "Invalid value, all/none should be set.";
79 return false;
80 }
81 if (config1.fps > 0 && config1.fps < config2.fps) {
82 RTC_LOG(LS_WARNING) << "Invalid fps/pixel value provided.";
83 return false;
84 }
Åsa Persson12314192019-06-20 15:45:07 +020085 return true;
Åsa Perssonf3d828e2019-05-06 12:22:49 +020086}
87
88bool IsValid(const std::vector<BalancedDegradationSettings::Config>& configs) {
89 if (configs.size() <= 1) {
90 RTC_LOG(LS_WARNING) << "Unsupported size, value ignored.";
91 return false;
92 }
93 for (const auto& config : configs) {
94 if (config.fps < kMinFps || config.fps > kMaxFps) {
95 RTC_LOG(LS_WARNING) << "Unsupported fps setting, value ignored.";
96 return false;
97 }
98 }
Åsa Persson1b247f12019-08-14 17:26:39 +020099 int last_kbps = configs[0].kbps;
100 for (size_t i = 1; i < configs.size(); ++i) {
101 if (configs[i].kbps > 0) {
102 if (configs[i].kbps < last_kbps) {
103 RTC_LOG(LS_WARNING) << "Invalid bitrate value provided.";
104 return false;
105 }
106 last_kbps = configs[i].kbps;
107 }
108 }
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200109 for (size_t i = 1; i < configs.size(); ++i) {
110 if (configs[i].pixels < configs[i - 1].pixels ||
111 configs[i].fps < configs[i - 1].fps) {
Åsa Persson12314192019-06-20 15:45:07 +0200112 RTC_LOG(LS_WARNING) << "Invalid fps/pixel value provided.";
113 return false;
114 }
Åsa Persson48284b82019-07-08 10:01:12 +0200115 if (!IsValid(configs[i].vp8, configs[i - 1].vp8) ||
116 !IsValid(configs[i].vp9, configs[i - 1].vp9) ||
117 !IsValid(configs[i].h264, configs[i - 1].h264) ||
118 !IsValid(configs[i].generic, configs[i - 1].generic)) {
Åsa Persson12314192019-06-20 15:45:07 +0200119 return false;
120 }
121 }
122 for (const auto& config : configs) {
Åsa Persson48284b82019-07-08 10:01:12 +0200123 if (!IsValidConfig(config.vp8) || !IsValidConfig(config.vp9) ||
124 !IsValidConfig(config.h264) || !IsValidConfig(config.generic)) {
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200125 return false;
126 }
127 }
128 return true;
129}
130
131std::vector<BalancedDegradationSettings::Config> GetValidOrDefault(
132 const std::vector<BalancedDegradationSettings::Config>& configs) {
133 if (IsValid(configs)) {
134 return configs;
135 }
136 return DefaultConfigs();
137}
Åsa Persson12314192019-06-20 15:45:07 +0200138
139absl::optional<VideoEncoder::QpThresholds> GetThresholds(
140 VideoCodecType type,
141 const BalancedDegradationSettings::Config& config) {
142 absl::optional<int> low;
143 absl::optional<int> high;
144
145 switch (type) {
146 case kVideoCodecVP8:
Åsa Persson48284b82019-07-08 10:01:12 +0200147 low = config.vp8.GetQpLow();
148 high = config.vp8.GetQpHigh();
Åsa Persson12314192019-06-20 15:45:07 +0200149 break;
150 case kVideoCodecVP9:
Åsa Persson48284b82019-07-08 10:01:12 +0200151 low = config.vp9.GetQpLow();
152 high = config.vp9.GetQpHigh();
Åsa Persson12314192019-06-20 15:45:07 +0200153 break;
154 case kVideoCodecH264:
Åsa Persson48284b82019-07-08 10:01:12 +0200155 low = config.h264.GetQpLow();
156 high = config.h264.GetQpHigh();
Åsa Persson12314192019-06-20 15:45:07 +0200157 break;
158 case kVideoCodecGeneric:
Åsa Persson48284b82019-07-08 10:01:12 +0200159 low = config.generic.GetQpLow();
160 high = config.generic.GetQpHigh();
Åsa Persson12314192019-06-20 15:45:07 +0200161 break;
162 default:
163 break;
164 }
165
166 if (low && high) {
167 RTC_LOG(LS_INFO) << "QP thresholds: low: " << *low << ", high: " << *high;
168 return absl::optional<VideoEncoder::QpThresholds>(
169 VideoEncoder::QpThresholds(*low, *high));
170 }
171 return absl::nullopt;
172}
Åsa Persson48284b82019-07-08 10:01:12 +0200173
174int GetFps(VideoCodecType type,
175 const absl::optional<BalancedDegradationSettings::Config>& config) {
176 if (!config.has_value()) {
177 return std::numeric_limits<int>::max();
178 }
179
180 absl::optional<int> fps;
181 switch (type) {
182 case kVideoCodecVP8:
183 fps = config->vp8.GetFps();
184 break;
185 case kVideoCodecVP9:
186 fps = config->vp9.GetFps();
187 break;
188 case kVideoCodecH264:
189 fps = config->h264.GetFps();
190 break;
191 case kVideoCodecGeneric:
192 fps = config->generic.GetFps();
193 break;
194 default:
195 break;
196 }
197
Åsa Persson0c38a862019-08-14 10:10:12 +0200198 const int framerate = fps.value_or(config->fps);
199
200 return (framerate == kMaxFps) ? std::numeric_limits<int>::max() : framerate;
Åsa Persson48284b82019-07-08 10:01:12 +0200201}
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200202} // namespace
203
Åsa Persson48284b82019-07-08 10:01:12 +0200204absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetQpLow()
205 const {
206 return (qp_low > 0) ? absl::optional<int>(qp_low) : absl::nullopt;
Åsa Persson12314192019-06-20 15:45:07 +0200207}
208
Åsa Persson48284b82019-07-08 10:01:12 +0200209absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetQpHigh()
210 const {
211 return (qp_high > 0) ? absl::optional<int>(qp_high) : absl::nullopt;
212}
213
214absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetFps()
215 const {
216 return (fps > 0) ? absl::optional<int>(fps) : absl::nullopt;
Åsa Persson12314192019-06-20 15:45:07 +0200217}
218
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200219BalancedDegradationSettings::Config::Config() = default;
220
Åsa Persson12314192019-06-20 15:45:07 +0200221BalancedDegradationSettings::Config::Config(int pixels,
222 int fps,
Åsa Persson1b247f12019-08-14 17:26:39 +0200223 int kbps,
Åsa Perssonf5e5d252019-08-16 17:24:59 +0200224 int fps_diff,
Åsa Persson48284b82019-07-08 10:01:12 +0200225 CodecTypeSpecific vp8,
226 CodecTypeSpecific vp9,
227 CodecTypeSpecific h264,
228 CodecTypeSpecific generic)
Åsa Persson12314192019-06-20 15:45:07 +0200229 : pixels(pixels),
230 fps(fps),
Åsa Persson1b247f12019-08-14 17:26:39 +0200231 kbps(kbps),
Åsa Perssonf5e5d252019-08-16 17:24:59 +0200232 fps_diff(fps_diff),
Åsa Persson12314192019-06-20 15:45:07 +0200233 vp8(vp8),
234 vp9(vp9),
235 h264(h264),
236 generic(generic) {}
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200237
238BalancedDegradationSettings::BalancedDegradationSettings() {
239 FieldTrialStructList<Config> configs(
240 {FieldTrialStructMember("pixels", [](Config* c) { return &c->pixels; }),
Åsa Persson12314192019-06-20 15:45:07 +0200241 FieldTrialStructMember("fps", [](Config* c) { return &c->fps; }),
Åsa Persson1b247f12019-08-14 17:26:39 +0200242 FieldTrialStructMember("kbps", [](Config* c) { return &c->kbps; }),
Åsa Perssonf5e5d252019-08-16 17:24:59 +0200243 FieldTrialStructMember("fps_diff",
244 [](Config* c) { return &c->fps_diff; }),
Åsa Persson12314192019-06-20 15:45:07 +0200245 FieldTrialStructMember("vp8_qp_low",
Åsa Persson48284b82019-07-08 10:01:12 +0200246 [](Config* c) { return &c->vp8.qp_low; }),
Åsa Persson12314192019-06-20 15:45:07 +0200247 FieldTrialStructMember("vp8_qp_high",
Åsa Persson48284b82019-07-08 10:01:12 +0200248 [](Config* c) { return &c->vp8.qp_high; }),
249 FieldTrialStructMember("vp8_fps", [](Config* c) { return &c->vp8.fps; }),
Åsa Persson12314192019-06-20 15:45:07 +0200250 FieldTrialStructMember("vp9_qp_low",
Åsa Persson48284b82019-07-08 10:01:12 +0200251 [](Config* c) { return &c->vp9.qp_low; }),
Åsa Persson12314192019-06-20 15:45:07 +0200252 FieldTrialStructMember("vp9_qp_high",
Åsa Persson48284b82019-07-08 10:01:12 +0200253 [](Config* c) { return &c->vp9.qp_high; }),
254 FieldTrialStructMember("vp9_fps", [](Config* c) { return &c->vp9.fps; }),
Åsa Persson12314192019-06-20 15:45:07 +0200255 FieldTrialStructMember("h264_qp_low",
Åsa Persson48284b82019-07-08 10:01:12 +0200256 [](Config* c) { return &c->h264.qp_low; }),
Åsa Persson12314192019-06-20 15:45:07 +0200257 FieldTrialStructMember("h264_qp_high",
Åsa Persson48284b82019-07-08 10:01:12 +0200258 [](Config* c) { return &c->h264.qp_high; }),
259 FieldTrialStructMember("h264_fps",
260 [](Config* c) { return &c->h264.fps; }),
Åsa Persson12314192019-06-20 15:45:07 +0200261 FieldTrialStructMember("generic_qp_low",
Åsa Persson48284b82019-07-08 10:01:12 +0200262 [](Config* c) { return &c->generic.qp_low; }),
Åsa Persson12314192019-06-20 15:45:07 +0200263 FieldTrialStructMember("generic_qp_high",
Åsa Persson48284b82019-07-08 10:01:12 +0200264 [](Config* c) { return &c->generic.qp_high; }),
265 FieldTrialStructMember("generic_fps",
266 [](Config* c) { return &c->generic.fps; })},
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200267 {});
268
269 ParseFieldTrial({&configs}, field_trial::FindFullName(kFieldTrial));
270
271 configs_ = GetValidOrDefault(configs.Get());
272 RTC_DCHECK_GT(configs_.size(), 1);
273}
274
275BalancedDegradationSettings::~BalancedDegradationSettings() {}
276
277std::vector<BalancedDegradationSettings::Config>
278BalancedDegradationSettings::GetConfigs() const {
279 return configs_;
280}
281
Åsa Persson48284b82019-07-08 10:01:12 +0200282int BalancedDegradationSettings::MinFps(VideoCodecType type, int pixels) const {
283 return GetFps(type, GetMinFpsConfig(pixels));
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200284}
285
Åsa Persson48284b82019-07-08 10:01:12 +0200286absl::optional<BalancedDegradationSettings::Config>
287BalancedDegradationSettings::GetMinFpsConfig(int pixels) const {
288 for (const auto& config : configs_) {
289 if (pixels <= config.pixels)
290 return config;
291 }
292 return absl::nullopt;
293}
294
295int BalancedDegradationSettings::MaxFps(VideoCodecType type, int pixels) const {
296 return GetFps(type, GetMaxFpsConfig(pixels));
297}
298
299absl::optional<BalancedDegradationSettings::Config>
300BalancedDegradationSettings::GetMaxFpsConfig(int pixels) const {
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200301 for (size_t i = 0; i < configs_.size() - 1; ++i) {
302 if (pixels <= configs_[i].pixels)
Åsa Persson48284b82019-07-08 10:01:12 +0200303 return configs_[i + 1];
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200304 }
Åsa Persson48284b82019-07-08 10:01:12 +0200305 return absl::nullopt;
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200306}
307
Åsa Persson1b247f12019-08-14 17:26:39 +0200308absl::optional<int> BalancedDegradationSettings::NextHigherBitrateKbps(
309 int pixels) const {
310 for (size_t i = 0; i < configs_.size() - 1; ++i) {
311 if (pixels <= configs_[i].pixels) {
312 return (configs_[i + 1].kbps > 0)
313 ? absl::optional<int>(configs_[i + 1].kbps)
314 : absl::nullopt;
315 }
316 }
317 return absl::nullopt;
318}
319
Åsa Persson4869bd62019-08-23 16:20:06 +0200320bool BalancedDegradationSettings::CanAdaptUp(int pixels,
321 uint32_t bitrate_bps) const {
322 absl::optional<int> next_layer_min_kbps = NextHigherBitrateKbps(pixels);
323 if (!next_layer_min_kbps.has_value() || bitrate_bps == 0) {
324 return true; // No limit configured or bitrate provided.
325 }
326 return bitrate_bps >=
327 static_cast<uint32_t>(next_layer_min_kbps.value() * 1000);
328}
329
Åsa Perssonf5e5d252019-08-16 17:24:59 +0200330absl::optional<int> BalancedDegradationSettings::MinFpsDiff(int pixels) const {
331 for (const auto& config : configs_) {
332 if (pixels <= config.pixels) {
333 return (config.fps_diff > kNoFpsDiff)
334 ? absl::optional<int>(config.fps_diff)
335 : absl::nullopt;
336 }
337 }
338 return absl::nullopt;
339}
340
Åsa Persson12314192019-06-20 15:45:07 +0200341absl::optional<VideoEncoder::QpThresholds>
342BalancedDegradationSettings::GetQpThresholds(VideoCodecType type,
343 int pixels) const {
344 return GetThresholds(type, GetConfig(pixels));
345}
346
347BalancedDegradationSettings::Config BalancedDegradationSettings::GetConfig(
348 int pixels) const {
349 for (const auto& config : configs_) {
350 if (pixels <= config.pixels)
351 return config;
352 }
353 return configs_.back(); // Use last above highest pixels.
354}
355
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200356} // namespace webrtc