blob: bfb0a5f1c0078d777b4c8d8bd598edf36cbc30f7 [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,
Åsa Persson30ab0152019-08-27 12:22:33 +020030 0,
Åsa Perssonf5e5d252019-08-16 17:24:59 +020031 BalancedDegradationSettings::kNoFpsDiff,
32 {0, 0, 0},
33 {0, 0, 0},
34 {0, 0, 0},
35 {0, 0, 0}},
36 {480 * 270,
37 10,
38 0,
Åsa Persson30ab0152019-08-27 12:22:33 +020039 0,
Åsa Perssonf5e5d252019-08-16 17:24:59 +020040 BalancedDegradationSettings::kNoFpsDiff,
41 {0, 0, 0},
42 {0, 0, 0},
43 {0, 0, 0},
44 {0, 0, 0}},
45 {640 * 480,
46 15,
47 0,
Åsa Persson30ab0152019-08-27 12:22:33 +020048 0,
Åsa Perssonf5e5d252019-08-16 17:24:59 +020049 BalancedDegradationSettings::kNoFpsDiff,
50 {0, 0, 0},
51 {0, 0, 0},
52 {0, 0, 0},
53 {0, 0, 0}}};
Åsa Persson12314192019-06-20 15:45:07 +020054}
55
Åsa Persson48284b82019-07-08 10:01:12 +020056bool IsValidConfig(
57 const BalancedDegradationSettings::CodecTypeSpecific& config) {
58 if (config.GetQpLow().has_value() != config.GetQpHigh().has_value()) {
59 RTC_LOG(LS_WARNING) << "Neither or both thresholds should be set.";
Åsa Persson12314192019-06-20 15:45:07 +020060 return false;
61 }
Åsa Persson48284b82019-07-08 10:01:12 +020062 if (config.GetQpLow().has_value() && config.GetQpHigh().has_value() &&
63 config.GetQpLow().value() >= config.GetQpHigh().value()) {
Åsa Persson12314192019-06-20 15:45:07 +020064 RTC_LOG(LS_WARNING) << "Invalid threshold value, low >= high threshold.";
65 return false;
66 }
Åsa Persson48284b82019-07-08 10:01:12 +020067 if (config.GetFps().has_value() && (config.GetFps().value() < kMinFps ||
68 config.GetFps().value() > kMaxFps)) {
69 RTC_LOG(LS_WARNING) << "Unsupported fps setting, value ignored.";
70 return false;
71 }
72 return true;
73}
74
75bool IsValid(const BalancedDegradationSettings::CodecTypeSpecific& config1,
76 const BalancedDegradationSettings::CodecTypeSpecific& config2) {
77 bool both_or_none_set = ((config1.qp_low > 0) == (config2.qp_low > 0) &&
78 (config1.qp_high > 0) == (config2.qp_high > 0) &&
79 (config1.fps > 0) == (config2.fps > 0));
80 if (!both_or_none_set) {
81 RTC_LOG(LS_WARNING) << "Invalid value, all/none should be set.";
82 return false;
83 }
84 if (config1.fps > 0 && config1.fps < config2.fps) {
85 RTC_LOG(LS_WARNING) << "Invalid fps/pixel value provided.";
86 return false;
87 }
Åsa Persson12314192019-06-20 15:45:07 +020088 return true;
Åsa Perssonf3d828e2019-05-06 12:22:49 +020089}
90
91bool IsValid(const std::vector<BalancedDegradationSettings::Config>& configs) {
92 if (configs.size() <= 1) {
93 RTC_LOG(LS_WARNING) << "Unsupported size, value ignored.";
94 return false;
95 }
96 for (const auto& config : configs) {
97 if (config.fps < kMinFps || config.fps > kMaxFps) {
98 RTC_LOG(LS_WARNING) << "Unsupported fps setting, value ignored.";
99 return false;
100 }
101 }
Åsa Persson1b247f12019-08-14 17:26:39 +0200102 int last_kbps = configs[0].kbps;
103 for (size_t i = 1; i < configs.size(); ++i) {
104 if (configs[i].kbps > 0) {
105 if (configs[i].kbps < last_kbps) {
106 RTC_LOG(LS_WARNING) << "Invalid bitrate value provided.";
107 return false;
108 }
109 last_kbps = configs[i].kbps;
110 }
111 }
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200112 for (size_t i = 1; i < configs.size(); ++i) {
113 if (configs[i].pixels < configs[i - 1].pixels ||
114 configs[i].fps < configs[i - 1].fps) {
Åsa Persson12314192019-06-20 15:45:07 +0200115 RTC_LOG(LS_WARNING) << "Invalid fps/pixel value provided.";
116 return false;
117 }
Åsa Persson48284b82019-07-08 10:01:12 +0200118 if (!IsValid(configs[i].vp8, configs[i - 1].vp8) ||
119 !IsValid(configs[i].vp9, configs[i - 1].vp9) ||
120 !IsValid(configs[i].h264, configs[i - 1].h264) ||
121 !IsValid(configs[i].generic, configs[i - 1].generic)) {
Åsa Persson12314192019-06-20 15:45:07 +0200122 return false;
123 }
124 }
125 for (const auto& config : configs) {
Åsa Persson48284b82019-07-08 10:01:12 +0200126 if (!IsValidConfig(config.vp8) || !IsValidConfig(config.vp9) ||
127 !IsValidConfig(config.h264) || !IsValidConfig(config.generic)) {
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200128 return false;
129 }
130 }
131 return true;
132}
133
134std::vector<BalancedDegradationSettings::Config> GetValidOrDefault(
135 const std::vector<BalancedDegradationSettings::Config>& configs) {
136 if (IsValid(configs)) {
137 return configs;
138 }
139 return DefaultConfigs();
140}
Åsa Persson12314192019-06-20 15:45:07 +0200141
142absl::optional<VideoEncoder::QpThresholds> GetThresholds(
143 VideoCodecType type,
144 const BalancedDegradationSettings::Config& config) {
145 absl::optional<int> low;
146 absl::optional<int> high;
147
148 switch (type) {
149 case kVideoCodecVP8:
Åsa Persson48284b82019-07-08 10:01:12 +0200150 low = config.vp8.GetQpLow();
151 high = config.vp8.GetQpHigh();
Åsa Persson12314192019-06-20 15:45:07 +0200152 break;
153 case kVideoCodecVP9:
Åsa Persson48284b82019-07-08 10:01:12 +0200154 low = config.vp9.GetQpLow();
155 high = config.vp9.GetQpHigh();
Åsa Persson12314192019-06-20 15:45:07 +0200156 break;
157 case kVideoCodecH264:
Åsa Persson48284b82019-07-08 10:01:12 +0200158 low = config.h264.GetQpLow();
159 high = config.h264.GetQpHigh();
Åsa Persson12314192019-06-20 15:45:07 +0200160 break;
161 case kVideoCodecGeneric:
Åsa Persson48284b82019-07-08 10:01:12 +0200162 low = config.generic.GetQpLow();
163 high = config.generic.GetQpHigh();
Åsa Persson12314192019-06-20 15:45:07 +0200164 break;
165 default:
166 break;
167 }
168
169 if (low && high) {
170 RTC_LOG(LS_INFO) << "QP thresholds: low: " << *low << ", high: " << *high;
171 return absl::optional<VideoEncoder::QpThresholds>(
172 VideoEncoder::QpThresholds(*low, *high));
173 }
174 return absl::nullopt;
175}
Åsa Persson48284b82019-07-08 10:01:12 +0200176
177int GetFps(VideoCodecType type,
178 const absl::optional<BalancedDegradationSettings::Config>& config) {
179 if (!config.has_value()) {
180 return std::numeric_limits<int>::max();
181 }
182
183 absl::optional<int> fps;
184 switch (type) {
185 case kVideoCodecVP8:
186 fps = config->vp8.GetFps();
187 break;
188 case kVideoCodecVP9:
189 fps = config->vp9.GetFps();
190 break;
191 case kVideoCodecH264:
192 fps = config->h264.GetFps();
193 break;
194 case kVideoCodecGeneric:
195 fps = config->generic.GetFps();
196 break;
197 default:
198 break;
199 }
200
Åsa Persson0c38a862019-08-14 10:10:12 +0200201 const int framerate = fps.value_or(config->fps);
202
203 return (framerate == kMaxFps) ? std::numeric_limits<int>::max() : framerate;
Åsa Persson48284b82019-07-08 10:01:12 +0200204}
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200205} // namespace
206
Åsa Persson48284b82019-07-08 10:01:12 +0200207absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetQpLow()
208 const {
209 return (qp_low > 0) ? absl::optional<int>(qp_low) : absl::nullopt;
Åsa Persson12314192019-06-20 15:45:07 +0200210}
211
Åsa Persson48284b82019-07-08 10:01:12 +0200212absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetQpHigh()
213 const {
214 return (qp_high > 0) ? absl::optional<int>(qp_high) : absl::nullopt;
215}
216
217absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetFps()
218 const {
219 return (fps > 0) ? absl::optional<int>(fps) : absl::nullopt;
Åsa Persson12314192019-06-20 15:45:07 +0200220}
221
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200222BalancedDegradationSettings::Config::Config() = default;
223
Åsa Persson12314192019-06-20 15:45:07 +0200224BalancedDegradationSettings::Config::Config(int pixels,
225 int fps,
Åsa Persson1b247f12019-08-14 17:26:39 +0200226 int kbps,
Åsa Persson30ab0152019-08-27 12:22:33 +0200227 int kbps_res,
Åsa Perssonf5e5d252019-08-16 17:24:59 +0200228 int fps_diff,
Åsa Persson48284b82019-07-08 10:01:12 +0200229 CodecTypeSpecific vp8,
230 CodecTypeSpecific vp9,
231 CodecTypeSpecific h264,
232 CodecTypeSpecific generic)
Åsa Persson12314192019-06-20 15:45:07 +0200233 : pixels(pixels),
234 fps(fps),
Åsa Persson1b247f12019-08-14 17:26:39 +0200235 kbps(kbps),
Åsa Persson30ab0152019-08-27 12:22:33 +0200236 kbps_res(kbps_res),
Åsa Perssonf5e5d252019-08-16 17:24:59 +0200237 fps_diff(fps_diff),
Åsa Persson12314192019-06-20 15:45:07 +0200238 vp8(vp8),
239 vp9(vp9),
240 h264(h264),
241 generic(generic) {}
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200242
243BalancedDegradationSettings::BalancedDegradationSettings() {
244 FieldTrialStructList<Config> configs(
245 {FieldTrialStructMember("pixels", [](Config* c) { return &c->pixels; }),
Åsa Persson12314192019-06-20 15:45:07 +0200246 FieldTrialStructMember("fps", [](Config* c) { return &c->fps; }),
Åsa Persson1b247f12019-08-14 17:26:39 +0200247 FieldTrialStructMember("kbps", [](Config* c) { return &c->kbps; }),
Åsa Persson30ab0152019-08-27 12:22:33 +0200248 FieldTrialStructMember("kbps_res",
249 [](Config* c) { return &c->kbps_res; }),
Åsa Perssonf5e5d252019-08-16 17:24:59 +0200250 FieldTrialStructMember("fps_diff",
251 [](Config* c) { return &c->fps_diff; }),
Åsa Persson12314192019-06-20 15:45:07 +0200252 FieldTrialStructMember("vp8_qp_low",
Åsa Persson48284b82019-07-08 10:01:12 +0200253 [](Config* c) { return &c->vp8.qp_low; }),
Åsa Persson12314192019-06-20 15:45:07 +0200254 FieldTrialStructMember("vp8_qp_high",
Åsa Persson48284b82019-07-08 10:01:12 +0200255 [](Config* c) { return &c->vp8.qp_high; }),
256 FieldTrialStructMember("vp8_fps", [](Config* c) { return &c->vp8.fps; }),
Åsa Persson12314192019-06-20 15:45:07 +0200257 FieldTrialStructMember("vp9_qp_low",
Åsa Persson48284b82019-07-08 10:01:12 +0200258 [](Config* c) { return &c->vp9.qp_low; }),
Åsa Persson12314192019-06-20 15:45:07 +0200259 FieldTrialStructMember("vp9_qp_high",
Åsa Persson48284b82019-07-08 10:01:12 +0200260 [](Config* c) { return &c->vp9.qp_high; }),
261 FieldTrialStructMember("vp9_fps", [](Config* c) { return &c->vp9.fps; }),
Åsa Persson12314192019-06-20 15:45:07 +0200262 FieldTrialStructMember("h264_qp_low",
Åsa Persson48284b82019-07-08 10:01:12 +0200263 [](Config* c) { return &c->h264.qp_low; }),
Åsa Persson12314192019-06-20 15:45:07 +0200264 FieldTrialStructMember("h264_qp_high",
Åsa Persson48284b82019-07-08 10:01:12 +0200265 [](Config* c) { return &c->h264.qp_high; }),
266 FieldTrialStructMember("h264_fps",
267 [](Config* c) { return &c->h264.fps; }),
Åsa Persson12314192019-06-20 15:45:07 +0200268 FieldTrialStructMember("generic_qp_low",
Åsa Persson48284b82019-07-08 10:01:12 +0200269 [](Config* c) { return &c->generic.qp_low; }),
Åsa Persson12314192019-06-20 15:45:07 +0200270 FieldTrialStructMember("generic_qp_high",
Åsa Persson48284b82019-07-08 10:01:12 +0200271 [](Config* c) { return &c->generic.qp_high; }),
272 FieldTrialStructMember("generic_fps",
273 [](Config* c) { return &c->generic.fps; })},
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200274 {});
275
276 ParseFieldTrial({&configs}, field_trial::FindFullName(kFieldTrial));
277
278 configs_ = GetValidOrDefault(configs.Get());
279 RTC_DCHECK_GT(configs_.size(), 1);
280}
281
282BalancedDegradationSettings::~BalancedDegradationSettings() {}
283
284std::vector<BalancedDegradationSettings::Config>
285BalancedDegradationSettings::GetConfigs() const {
286 return configs_;
287}
288
Åsa Persson48284b82019-07-08 10:01:12 +0200289int BalancedDegradationSettings::MinFps(VideoCodecType type, int pixels) const {
290 return GetFps(type, GetMinFpsConfig(pixels));
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200291}
292
Åsa Persson48284b82019-07-08 10:01:12 +0200293absl::optional<BalancedDegradationSettings::Config>
294BalancedDegradationSettings::GetMinFpsConfig(int pixels) const {
295 for (const auto& config : configs_) {
296 if (pixels <= config.pixels)
297 return config;
298 }
299 return absl::nullopt;
300}
301
302int BalancedDegradationSettings::MaxFps(VideoCodecType type, int pixels) const {
303 return GetFps(type, GetMaxFpsConfig(pixels));
304}
305
306absl::optional<BalancedDegradationSettings::Config>
307BalancedDegradationSettings::GetMaxFpsConfig(int pixels) const {
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200308 for (size_t i = 0; i < configs_.size() - 1; ++i) {
309 if (pixels <= configs_[i].pixels)
Åsa Persson48284b82019-07-08 10:01:12 +0200310 return configs_[i + 1];
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200311 }
Åsa Persson48284b82019-07-08 10:01:12 +0200312 return absl::nullopt;
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200313}
314
Åsa Persson1b247f12019-08-14 17:26:39 +0200315absl::optional<int> BalancedDegradationSettings::NextHigherBitrateKbps(
316 int pixels) const {
317 for (size_t i = 0; i < configs_.size() - 1; ++i) {
318 if (pixels <= configs_[i].pixels) {
319 return (configs_[i + 1].kbps > 0)
320 ? absl::optional<int>(configs_[i + 1].kbps)
321 : absl::nullopt;
322 }
323 }
324 return absl::nullopt;
325}
326
Åsa Persson30ab0152019-08-27 12:22:33 +0200327absl::optional<int>
328BalancedDegradationSettings::ResolutionNextHigherBitrateKbps(int pixels) const {
329 for (size_t i = 0; i < configs_.size() - 1; ++i) {
330 if (pixels <= configs_[i].pixels) {
331 return (configs_[i + 1].kbps_res > 0)
332 ? absl::optional<int>(configs_[i + 1].kbps_res)
333 : absl::nullopt;
334 }
335 }
336 return absl::nullopt;
337}
338
Åsa Persson4869bd62019-08-23 16:20:06 +0200339bool BalancedDegradationSettings::CanAdaptUp(int pixels,
340 uint32_t bitrate_bps) const {
341 absl::optional<int> next_layer_min_kbps = NextHigherBitrateKbps(pixels);
342 if (!next_layer_min_kbps.has_value() || bitrate_bps == 0) {
343 return true; // No limit configured or bitrate provided.
344 }
345 return bitrate_bps >=
346 static_cast<uint32_t>(next_layer_min_kbps.value() * 1000);
347}
348
Åsa Persson30ab0152019-08-27 12:22:33 +0200349bool BalancedDegradationSettings::CanAdaptUpResolution(
350 int pixels,
351 uint32_t bitrate_bps) const {
352 absl::optional<int> next_layer_min_kbps =
353 ResolutionNextHigherBitrateKbps(pixels);
354 if (!next_layer_min_kbps.has_value() || bitrate_bps == 0) {
355 return true; // No limit configured or bitrate provided.
356 }
357 return bitrate_bps >=
358 static_cast<uint32_t>(next_layer_min_kbps.value() * 1000);
359}
360
Åsa Perssonf5e5d252019-08-16 17:24:59 +0200361absl::optional<int> BalancedDegradationSettings::MinFpsDiff(int pixels) const {
362 for (const auto& config : configs_) {
363 if (pixels <= config.pixels) {
364 return (config.fps_diff > kNoFpsDiff)
365 ? absl::optional<int>(config.fps_diff)
366 : absl::nullopt;
367 }
368 }
369 return absl::nullopt;
370}
371
Åsa Persson12314192019-06-20 15:45:07 +0200372absl::optional<VideoEncoder::QpThresholds>
373BalancedDegradationSettings::GetQpThresholds(VideoCodecType type,
374 int pixels) const {
375 return GetThresholds(type, GetConfig(pixels));
376}
377
378BalancedDegradationSettings::Config BalancedDegradationSettings::GetConfig(
379 int pixels) const {
380 for (const auto& config : configs_) {
381 if (pixels <= config.pixels)
382 return config;
383 }
384 return configs_.back(); // Use last above highest pixels.
385}
386
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200387} // namespace webrtc