1 /*
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"
12
13 #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
20 namespace webrtc {
21 namespace {
22 constexpr char kFieldTrial[] = "WebRTC-Video-BalancedDegradationSettings";
23 constexpr int kMinFps = 1;
24 constexpr int kMaxFps = 100; // 100 means unlimited fps.
25
DefaultConfigs()26 std::vector<BalancedDegradationSettings::Config> DefaultConfigs() {
27 return {{320 * 240,
28 7,
29 0,
30 0,
31 BalancedDegradationSettings::kNoFpsDiff,
32 {0, 0, 0, 0, 0},
33 {0, 0, 0, 0, 0},
34 {0, 0, 0, 0, 0},
35 {0, 0, 0, 0, 0},
36 {0, 0, 0, 0, 0}},
37 {480 * 270,
38 10,
39 0,
40 0,
41 BalancedDegradationSettings::kNoFpsDiff,
42 {0, 0, 0, 0, 0},
43 {0, 0, 0, 0, 0},
44 {0, 0, 0, 0, 0},
45 {0, 0, 0, 0, 0},
46 {0, 0, 0, 0, 0}},
47 {640 * 480,
48 15,
49 0,
50 0,
51 BalancedDegradationSettings::kNoFpsDiff,
52 {0, 0, 0, 0, 0},
53 {0, 0, 0, 0, 0},
54 {0, 0, 0, 0, 0},
55 {0, 0, 0, 0, 0},
56 {0, 0, 0, 0, 0}}};
57 }
58
IsValidConfig(const BalancedDegradationSettings::CodecTypeSpecific & config)59 bool IsValidConfig(
60 const BalancedDegradationSettings::CodecTypeSpecific& config) {
61 if (config.GetQpLow().has_value() != config.GetQpHigh().has_value()) {
62 RTC_LOG(LS_WARNING) << "Neither or both thresholds should be set.";
63 return false;
64 }
65 if (config.GetQpLow().has_value() && config.GetQpHigh().has_value() &&
66 config.GetQpLow().value() >= config.GetQpHigh().value()) {
67 RTC_LOG(LS_WARNING) << "Invalid threshold value, low >= high threshold.";
68 return false;
69 }
70 if (config.GetFps().has_value() && (config.GetFps().value() < kMinFps ||
71 config.GetFps().value() > kMaxFps)) {
72 RTC_LOG(LS_WARNING) << "Unsupported fps setting, value ignored.";
73 return false;
74 }
75 return true;
76 }
77
IsValid(const BalancedDegradationSettings::CodecTypeSpecific & config1,const BalancedDegradationSettings::CodecTypeSpecific & config2)78 bool IsValid(const BalancedDegradationSettings::CodecTypeSpecific& config1,
79 const BalancedDegradationSettings::CodecTypeSpecific& config2) {
80 bool both_or_none_set = ((config1.qp_low > 0) == (config2.qp_low > 0) &&
81 (config1.qp_high > 0) == (config2.qp_high > 0) &&
82 (config1.fps > 0) == (config2.fps > 0));
83 if (!both_or_none_set) {
84 RTC_LOG(LS_WARNING) << "Invalid value, all/none should be set.";
85 return false;
86 }
87 if (config1.fps > 0 && config1.fps < config2.fps) {
88 RTC_LOG(LS_WARNING) << "Invalid fps/pixel value provided.";
89 return false;
90 }
91 return true;
92 }
93
IsValid(const std::vector<BalancedDegradationSettings::Config> & configs)94 bool IsValid(const std::vector<BalancedDegradationSettings::Config>& configs) {
95 if (configs.size() <= 1) {
96 RTC_LOG(LS_WARNING) << "Unsupported size, value ignored.";
97 return false;
98 }
99 for (const auto& config : configs) {
100 if (config.fps < kMinFps || config.fps > kMaxFps) {
101 RTC_LOG(LS_WARNING) << "Unsupported fps setting, value ignored.";
102 return false;
103 }
104 }
105 int last_kbps = configs[0].kbps;
106 for (size_t i = 1; i < configs.size(); ++i) {
107 if (configs[i].kbps > 0) {
108 if (configs[i].kbps < last_kbps) {
109 RTC_LOG(LS_WARNING) << "Invalid bitrate value provided.";
110 return false;
111 }
112 last_kbps = configs[i].kbps;
113 }
114 }
115 for (size_t i = 1; i < configs.size(); ++i) {
116 if (configs[i].pixels < configs[i - 1].pixels ||
117 configs[i].fps < configs[i - 1].fps) {
118 RTC_LOG(LS_WARNING) << "Invalid fps/pixel value provided.";
119 return false;
120 }
121 if (!IsValid(configs[i].vp8, configs[i - 1].vp8) ||
122 !IsValid(configs[i].vp9, configs[i - 1].vp9) ||
123 !IsValid(configs[i].h264, configs[i - 1].h264) ||
124 !IsValid(configs[i].av1, configs[i - 1].av1) ||
125 !IsValid(configs[i].generic, configs[i - 1].generic)) {
126 return false;
127 }
128 }
129 for (const auto& config : configs) {
130 if (!IsValidConfig(config.vp8) || !IsValidConfig(config.vp9) ||
131 !IsValidConfig(config.h264) || !IsValidConfig(config.av1) ||
132 !IsValidConfig(config.generic)) {
133 return false;
134 }
135 }
136 return true;
137 }
138
GetValidOrDefault(const std::vector<BalancedDegradationSettings::Config> & configs)139 std::vector<BalancedDegradationSettings::Config> GetValidOrDefault(
140 const std::vector<BalancedDegradationSettings::Config>& configs) {
141 if (IsValid(configs)) {
142 return configs;
143 }
144 return DefaultConfigs();
145 }
146
GetThresholds(VideoCodecType type,const BalancedDegradationSettings::Config & config)147 absl::optional<VideoEncoder::QpThresholds> GetThresholds(
148 VideoCodecType type,
149 const BalancedDegradationSettings::Config& config) {
150 absl::optional<int> low;
151 absl::optional<int> high;
152
153 switch (type) {
154 case kVideoCodecVP8:
155 low = config.vp8.GetQpLow();
156 high = config.vp8.GetQpHigh();
157 break;
158 case kVideoCodecVP9:
159 low = config.vp9.GetQpLow();
160 high = config.vp9.GetQpHigh();
161 break;
162 case kVideoCodecH264:
163 low = config.h264.GetQpLow();
164 high = config.h264.GetQpHigh();
165 break;
166 case kVideoCodecAV1:
167 low = config.av1.GetQpLow();
168 high = config.av1.GetQpHigh();
169 break;
170 case kVideoCodecGeneric:
171 low = config.generic.GetQpLow();
172 high = config.generic.GetQpHigh();
173 break;
174 default:
175 break;
176 }
177
178 if (low && high) {
179 RTC_LOG(LS_INFO) << "QP thresholds: low: " << *low << ", high: " << *high;
180 return absl::optional<VideoEncoder::QpThresholds>(
181 VideoEncoder::QpThresholds(*low, *high));
182 }
183 return absl::nullopt;
184 }
185
GetFps(VideoCodecType type,const absl::optional<BalancedDegradationSettings::Config> & config)186 int GetFps(VideoCodecType type,
187 const absl::optional<BalancedDegradationSettings::Config>& config) {
188 if (!config.has_value()) {
189 return std::numeric_limits<int>::max();
190 }
191
192 absl::optional<int> fps;
193 switch (type) {
194 case kVideoCodecVP8:
195 fps = config->vp8.GetFps();
196 break;
197 case kVideoCodecVP9:
198 fps = config->vp9.GetFps();
199 break;
200 case kVideoCodecH264:
201 fps = config->h264.GetFps();
202 break;
203 case kVideoCodecAV1:
204 fps = config->av1.GetFps();
205 break;
206 case kVideoCodecGeneric:
207 fps = config->generic.GetFps();
208 break;
209 default:
210 break;
211 }
212
213 const int framerate = fps.value_or(config->fps);
214
215 return (framerate == kMaxFps) ? std::numeric_limits<int>::max() : framerate;
216 }
217
GetKbps(VideoCodecType type,const absl::optional<BalancedDegradationSettings::Config> & config)218 absl::optional<int> GetKbps(
219 VideoCodecType type,
220 const absl::optional<BalancedDegradationSettings::Config>& config) {
221 if (!config.has_value())
222 return absl::nullopt;
223
224 absl::optional<int> kbps;
225 switch (type) {
226 case kVideoCodecVP8:
227 kbps = config->vp8.GetKbps();
228 break;
229 case kVideoCodecVP9:
230 kbps = config->vp9.GetKbps();
231 break;
232 case kVideoCodecH264:
233 kbps = config->h264.GetKbps();
234 break;
235 case kVideoCodecAV1:
236 kbps = config->av1.GetKbps();
237 break;
238 case kVideoCodecGeneric:
239 kbps = config->generic.GetKbps();
240 break;
241 default:
242 break;
243 }
244
245 if (kbps.has_value())
246 return kbps;
247
248 return config->kbps > 0 ? absl::optional<int>(config->kbps) : absl::nullopt;
249 }
250
GetKbpsRes(VideoCodecType type,const absl::optional<BalancedDegradationSettings::Config> & config)251 absl::optional<int> GetKbpsRes(
252 VideoCodecType type,
253 const absl::optional<BalancedDegradationSettings::Config>& config) {
254 if (!config.has_value())
255 return absl::nullopt;
256
257 absl::optional<int> kbps_res;
258 switch (type) {
259 case kVideoCodecVP8:
260 kbps_res = config->vp8.GetKbpsRes();
261 break;
262 case kVideoCodecVP9:
263 kbps_res = config->vp9.GetKbpsRes();
264 break;
265 case kVideoCodecH264:
266 kbps_res = config->h264.GetKbpsRes();
267 break;
268 case kVideoCodecAV1:
269 kbps_res = config->av1.GetKbpsRes();
270 break;
271 case kVideoCodecGeneric:
272 kbps_res = config->generic.GetKbpsRes();
273 break;
274 default:
275 break;
276 }
277
278 if (kbps_res.has_value())
279 return kbps_res;
280
281 return config->kbps_res > 0 ? absl::optional<int>(config->kbps_res)
282 : absl::nullopt;
283 }
284 } // namespace
285
GetQpLow() const286 absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetQpLow()
287 const {
288 return (qp_low > 0) ? absl::optional<int>(qp_low) : absl::nullopt;
289 }
290
GetQpHigh() const291 absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetQpHigh()
292 const {
293 return (qp_high > 0) ? absl::optional<int>(qp_high) : absl::nullopt;
294 }
295
GetFps() const296 absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetFps()
297 const {
298 return (fps > 0) ? absl::optional<int>(fps) : absl::nullopt;
299 }
300
GetKbps() const301 absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetKbps()
302 const {
303 return (kbps > 0) ? absl::optional<int>(kbps) : absl::nullopt;
304 }
305
GetKbpsRes() const306 absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetKbpsRes()
307 const {
308 return (kbps_res > 0) ? absl::optional<int>(kbps_res) : absl::nullopt;
309 }
310
311 BalancedDegradationSettings::Config::Config() = default;
312
Config(int pixels,int fps,int kbps,int kbps_res,int fps_diff,CodecTypeSpecific vp8,CodecTypeSpecific vp9,CodecTypeSpecific h264,CodecTypeSpecific av1,CodecTypeSpecific generic)313 BalancedDegradationSettings::Config::Config(int pixels,
314 int fps,
315 int kbps,
316 int kbps_res,
317 int fps_diff,
318 CodecTypeSpecific vp8,
319 CodecTypeSpecific vp9,
320 CodecTypeSpecific h264,
321 CodecTypeSpecific av1,
322 CodecTypeSpecific generic)
323 : pixels(pixels),
324 fps(fps),
325 kbps(kbps),
326 kbps_res(kbps_res),
327 fps_diff(fps_diff),
328 vp8(vp8),
329 vp9(vp9),
330 h264(h264),
331 av1(av1),
332 generic(generic) {}
333
BalancedDegradationSettings()334 BalancedDegradationSettings::BalancedDegradationSettings() {
335 FieldTrialStructList<Config> configs(
336 {FieldTrialStructMember("pixels", [](Config* c) { return &c->pixels; }),
337 FieldTrialStructMember("fps", [](Config* c) { return &c->fps; }),
338 FieldTrialStructMember("kbps", [](Config* c) { return &c->kbps; }),
339 FieldTrialStructMember("kbps_res",
340 [](Config* c) { return &c->kbps_res; }),
341 FieldTrialStructMember("fps_diff",
342 [](Config* c) { return &c->fps_diff; }),
343 FieldTrialStructMember("vp8_qp_low",
344 [](Config* c) { return &c->vp8.qp_low; }),
345 FieldTrialStructMember("vp8_qp_high",
346 [](Config* c) { return &c->vp8.qp_high; }),
347 FieldTrialStructMember("vp8_fps", [](Config* c) { return &c->vp8.fps; }),
348 FieldTrialStructMember("vp8_kbps",
349 [](Config* c) { return &c->vp8.kbps; }),
350 FieldTrialStructMember("vp8_kbps_res",
351 [](Config* c) { return &c->vp8.kbps_res; }),
352 FieldTrialStructMember("vp9_qp_low",
353 [](Config* c) { return &c->vp9.qp_low; }),
354 FieldTrialStructMember("vp9_qp_high",
355 [](Config* c) { return &c->vp9.qp_high; }),
356 FieldTrialStructMember("vp9_fps", [](Config* c) { return &c->vp9.fps; }),
357 FieldTrialStructMember("vp9_kbps",
358 [](Config* c) { return &c->vp9.kbps; }),
359 FieldTrialStructMember("vp9_kbps_res",
360 [](Config* c) { return &c->vp9.kbps_res; }),
361 FieldTrialStructMember("h264_qp_low",
362 [](Config* c) { return &c->h264.qp_low; }),
363 FieldTrialStructMember("h264_qp_high",
364 [](Config* c) { return &c->h264.qp_high; }),
365 FieldTrialStructMember("h264_fps",
366 [](Config* c) { return &c->h264.fps; }),
367 FieldTrialStructMember("h264_kbps",
368 [](Config* c) { return &c->h264.kbps; }),
369 FieldTrialStructMember("h264_kbps_res",
370 [](Config* c) { return &c->h264.kbps_res; }),
371 FieldTrialStructMember("av1_qp_low",
372 [](Config* c) { return &c->av1.qp_low; }),
373 FieldTrialStructMember("av1_qp_high",
374 [](Config* c) { return &c->av1.qp_high; }),
375 FieldTrialStructMember("av1_fps", [](Config* c) { return &c->av1.fps; }),
376 FieldTrialStructMember("av1_kbps",
377 [](Config* c) { return &c->av1.kbps; }),
378 FieldTrialStructMember("av1_kbps_res",
379 [](Config* c) { return &c->av1.kbps_res; }),
380 FieldTrialStructMember("generic_qp_low",
381 [](Config* c) { return &c->generic.qp_low; }),
382 FieldTrialStructMember("generic_qp_high",
383 [](Config* c) { return &c->generic.qp_high; }),
384 FieldTrialStructMember("generic_fps",
385 [](Config* c) { return &c->generic.fps; }),
386 FieldTrialStructMember("generic_kbps",
387 [](Config* c) { return &c->generic.kbps; }),
388 FieldTrialStructMember("generic_kbps_res",
389 [](Config* c) { return &c->generic.kbps_res; })},
390 {});
391
392 ParseFieldTrial({&configs}, field_trial::FindFullName(kFieldTrial));
393
394 configs_ = GetValidOrDefault(configs.Get());
395 RTC_DCHECK_GT(configs_.size(), 1);
396 }
397
~BalancedDegradationSettings()398 BalancedDegradationSettings::~BalancedDegradationSettings() {}
399
400 std::vector<BalancedDegradationSettings::Config>
GetConfigs() const401 BalancedDegradationSettings::GetConfigs() const {
402 return configs_;
403 }
404
MinFps(VideoCodecType type,int pixels) const405 int BalancedDegradationSettings::MinFps(VideoCodecType type, int pixels) const {
406 return GetFps(type, GetMinFpsConfig(pixels));
407 }
408
409 absl::optional<BalancedDegradationSettings::Config>
GetMinFpsConfig(int pixels) const410 BalancedDegradationSettings::GetMinFpsConfig(int pixels) const {
411 for (const auto& config : configs_) {
412 if (pixels <= config.pixels)
413 return config;
414 }
415 return absl::nullopt;
416 }
417
MaxFps(VideoCodecType type,int pixels) const418 int BalancedDegradationSettings::MaxFps(VideoCodecType type, int pixels) const {
419 return GetFps(type, GetMaxFpsConfig(pixels));
420 }
421
422 absl::optional<BalancedDegradationSettings::Config>
GetMaxFpsConfig(int pixels) const423 BalancedDegradationSettings::GetMaxFpsConfig(int pixels) const {
424 for (size_t i = 0; i < configs_.size() - 1; ++i) {
425 if (pixels <= configs_[i].pixels)
426 return configs_[i + 1];
427 }
428 return absl::nullopt;
429 }
430
CanAdaptUp(VideoCodecType type,int pixels,uint32_t bitrate_bps) const431 bool BalancedDegradationSettings::CanAdaptUp(VideoCodecType type,
432 int pixels,
433 uint32_t bitrate_bps) const {
434 absl::optional<int> min_kbps = GetKbps(type, GetMaxFpsConfig(pixels));
435 if (!min_kbps.has_value() || bitrate_bps == 0) {
436 return true; // No limit configured or bitrate provided.
437 }
438 return bitrate_bps >= static_cast<uint32_t>(min_kbps.value() * 1000);
439 }
440
CanAdaptUpResolution(VideoCodecType type,int pixels,uint32_t bitrate_bps) const441 bool BalancedDegradationSettings::CanAdaptUpResolution(
442 VideoCodecType type,
443 int pixels,
444 uint32_t bitrate_bps) const {
445 absl::optional<int> min_kbps = GetKbpsRes(type, GetMaxFpsConfig(pixels));
446 if (!min_kbps.has_value() || bitrate_bps == 0) {
447 return true; // No limit configured or bitrate provided.
448 }
449 return bitrate_bps >= static_cast<uint32_t>(min_kbps.value() * 1000);
450 }
451
MinFpsDiff(int pixels) const452 absl::optional<int> BalancedDegradationSettings::MinFpsDiff(int pixels) const {
453 for (const auto& config : configs_) {
454 if (pixels <= config.pixels) {
455 return (config.fps_diff > kNoFpsDiff)
456 ? absl::optional<int>(config.fps_diff)
457 : absl::nullopt;
458 }
459 }
460 return absl::nullopt;
461 }
462
463 absl::optional<VideoEncoder::QpThresholds>
GetQpThresholds(VideoCodecType type,int pixels) const464 BalancedDegradationSettings::GetQpThresholds(VideoCodecType type,
465 int pixels) const {
466 return GetThresholds(type, GetConfig(pixels));
467 }
468
GetConfig(int pixels) const469 BalancedDegradationSettings::Config BalancedDegradationSettings::GetConfig(
470 int pixels) const {
471 for (const auto& config : configs_) {
472 if (pixels <= config.pixels)
473 return config;
474 }
475 return configs_.back(); // Use last above highest pixels.
476 }
477
478 } // namespace webrtc
479