blob: 3dda1344b8ae8fcfad672c0f9d8785a3848e5857 [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 Persson1b247f12019-08-14 17:26:39 +020027 return {{320 * 240, 7, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
28 {480 * 270, 10, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
29 {640 * 480, 15, 0, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}};
Åsa Persson12314192019-06-20 15:45:07 +020030}
31
Åsa Persson48284b82019-07-08 10:01:12 +020032bool IsValidConfig(
33 const BalancedDegradationSettings::CodecTypeSpecific& config) {
34 if (config.GetQpLow().has_value() != config.GetQpHigh().has_value()) {
35 RTC_LOG(LS_WARNING) << "Neither or both thresholds should be set.";
Åsa Persson12314192019-06-20 15:45:07 +020036 return false;
37 }
Åsa Persson48284b82019-07-08 10:01:12 +020038 if (config.GetQpLow().has_value() && config.GetQpHigh().has_value() &&
39 config.GetQpLow().value() >= config.GetQpHigh().value()) {
Åsa Persson12314192019-06-20 15:45:07 +020040 RTC_LOG(LS_WARNING) << "Invalid threshold value, low >= high threshold.";
41 return false;
42 }
Åsa Persson48284b82019-07-08 10:01:12 +020043 if (config.GetFps().has_value() && (config.GetFps().value() < kMinFps ||
44 config.GetFps().value() > kMaxFps)) {
45 RTC_LOG(LS_WARNING) << "Unsupported fps setting, value ignored.";
46 return false;
47 }
48 return true;
49}
50
51bool IsValid(const BalancedDegradationSettings::CodecTypeSpecific& config1,
52 const BalancedDegradationSettings::CodecTypeSpecific& config2) {
53 bool both_or_none_set = ((config1.qp_low > 0) == (config2.qp_low > 0) &&
54 (config1.qp_high > 0) == (config2.qp_high > 0) &&
55 (config1.fps > 0) == (config2.fps > 0));
56 if (!both_or_none_set) {
57 RTC_LOG(LS_WARNING) << "Invalid value, all/none should be set.";
58 return false;
59 }
60 if (config1.fps > 0 && config1.fps < config2.fps) {
61 RTC_LOG(LS_WARNING) << "Invalid fps/pixel value provided.";
62 return false;
63 }
Åsa Persson12314192019-06-20 15:45:07 +020064 return true;
Åsa Perssonf3d828e2019-05-06 12:22:49 +020065}
66
67bool IsValid(const std::vector<BalancedDegradationSettings::Config>& configs) {
68 if (configs.size() <= 1) {
69 RTC_LOG(LS_WARNING) << "Unsupported size, value ignored.";
70 return false;
71 }
72 for (const auto& config : configs) {
73 if (config.fps < kMinFps || config.fps > kMaxFps) {
74 RTC_LOG(LS_WARNING) << "Unsupported fps setting, value ignored.";
75 return false;
76 }
77 }
Åsa Persson1b247f12019-08-14 17:26:39 +020078 int last_kbps = configs[0].kbps;
79 for (size_t i = 1; i < configs.size(); ++i) {
80 if (configs[i].kbps > 0) {
81 if (configs[i].kbps < last_kbps) {
82 RTC_LOG(LS_WARNING) << "Invalid bitrate value provided.";
83 return false;
84 }
85 last_kbps = configs[i].kbps;
86 }
87 }
Åsa Perssonf3d828e2019-05-06 12:22:49 +020088 for (size_t i = 1; i < configs.size(); ++i) {
89 if (configs[i].pixels < configs[i - 1].pixels ||
90 configs[i].fps < configs[i - 1].fps) {
Åsa Persson12314192019-06-20 15:45:07 +020091 RTC_LOG(LS_WARNING) << "Invalid fps/pixel value provided.";
92 return false;
93 }
Åsa Persson48284b82019-07-08 10:01:12 +020094 if (!IsValid(configs[i].vp8, configs[i - 1].vp8) ||
95 !IsValid(configs[i].vp9, configs[i - 1].vp9) ||
96 !IsValid(configs[i].h264, configs[i - 1].h264) ||
97 !IsValid(configs[i].generic, configs[i - 1].generic)) {
Åsa Persson12314192019-06-20 15:45:07 +020098 return false;
99 }
100 }
101 for (const auto& config : configs) {
Åsa Persson48284b82019-07-08 10:01:12 +0200102 if (!IsValidConfig(config.vp8) || !IsValidConfig(config.vp9) ||
103 !IsValidConfig(config.h264) || !IsValidConfig(config.generic)) {
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200104 return false;
105 }
106 }
107 return true;
108}
109
110std::vector<BalancedDegradationSettings::Config> GetValidOrDefault(
111 const std::vector<BalancedDegradationSettings::Config>& configs) {
112 if (IsValid(configs)) {
113 return configs;
114 }
115 return DefaultConfigs();
116}
Åsa Persson12314192019-06-20 15:45:07 +0200117
118absl::optional<VideoEncoder::QpThresholds> GetThresholds(
119 VideoCodecType type,
120 const BalancedDegradationSettings::Config& config) {
121 absl::optional<int> low;
122 absl::optional<int> high;
123
124 switch (type) {
125 case kVideoCodecVP8:
Åsa Persson48284b82019-07-08 10:01:12 +0200126 low = config.vp8.GetQpLow();
127 high = config.vp8.GetQpHigh();
Åsa Persson12314192019-06-20 15:45:07 +0200128 break;
129 case kVideoCodecVP9:
Åsa Persson48284b82019-07-08 10:01:12 +0200130 low = config.vp9.GetQpLow();
131 high = config.vp9.GetQpHigh();
Åsa Persson12314192019-06-20 15:45:07 +0200132 break;
133 case kVideoCodecH264:
Åsa Persson48284b82019-07-08 10:01:12 +0200134 low = config.h264.GetQpLow();
135 high = config.h264.GetQpHigh();
Åsa Persson12314192019-06-20 15:45:07 +0200136 break;
137 case kVideoCodecGeneric:
Åsa Persson48284b82019-07-08 10:01:12 +0200138 low = config.generic.GetQpLow();
139 high = config.generic.GetQpHigh();
Åsa Persson12314192019-06-20 15:45:07 +0200140 break;
141 default:
142 break;
143 }
144
145 if (low && high) {
146 RTC_LOG(LS_INFO) << "QP thresholds: low: " << *low << ", high: " << *high;
147 return absl::optional<VideoEncoder::QpThresholds>(
148 VideoEncoder::QpThresholds(*low, *high));
149 }
150 return absl::nullopt;
151}
Åsa Persson48284b82019-07-08 10:01:12 +0200152
153int GetFps(VideoCodecType type,
154 const absl::optional<BalancedDegradationSettings::Config>& config) {
155 if (!config.has_value()) {
156 return std::numeric_limits<int>::max();
157 }
158
159 absl::optional<int> fps;
160 switch (type) {
161 case kVideoCodecVP8:
162 fps = config->vp8.GetFps();
163 break;
164 case kVideoCodecVP9:
165 fps = config->vp9.GetFps();
166 break;
167 case kVideoCodecH264:
168 fps = config->h264.GetFps();
169 break;
170 case kVideoCodecGeneric:
171 fps = config->generic.GetFps();
172 break;
173 default:
174 break;
175 }
176
Åsa Persson0c38a862019-08-14 10:10:12 +0200177 const int framerate = fps.value_or(config->fps);
178
179 return (framerate == kMaxFps) ? std::numeric_limits<int>::max() : framerate;
Åsa Persson48284b82019-07-08 10:01:12 +0200180}
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200181} // namespace
182
Åsa Persson48284b82019-07-08 10:01:12 +0200183absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetQpLow()
184 const {
185 return (qp_low > 0) ? absl::optional<int>(qp_low) : absl::nullopt;
Åsa Persson12314192019-06-20 15:45:07 +0200186}
187
Åsa Persson48284b82019-07-08 10:01:12 +0200188absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetQpHigh()
189 const {
190 return (qp_high > 0) ? absl::optional<int>(qp_high) : absl::nullopt;
191}
192
193absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetFps()
194 const {
195 return (fps > 0) ? absl::optional<int>(fps) : absl::nullopt;
Åsa Persson12314192019-06-20 15:45:07 +0200196}
197
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200198BalancedDegradationSettings::Config::Config() = default;
199
Åsa Persson12314192019-06-20 15:45:07 +0200200BalancedDegradationSettings::Config::Config(int pixels,
201 int fps,
Åsa Persson1b247f12019-08-14 17:26:39 +0200202 int kbps,
Åsa Persson48284b82019-07-08 10:01:12 +0200203 CodecTypeSpecific vp8,
204 CodecTypeSpecific vp9,
205 CodecTypeSpecific h264,
206 CodecTypeSpecific generic)
Åsa Persson12314192019-06-20 15:45:07 +0200207 : pixels(pixels),
208 fps(fps),
Åsa Persson1b247f12019-08-14 17:26:39 +0200209 kbps(kbps),
Åsa Persson12314192019-06-20 15:45:07 +0200210 vp8(vp8),
211 vp9(vp9),
212 h264(h264),
213 generic(generic) {}
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200214
215BalancedDegradationSettings::BalancedDegradationSettings() {
216 FieldTrialStructList<Config> configs(
217 {FieldTrialStructMember("pixels", [](Config* c) { return &c->pixels; }),
Åsa Persson12314192019-06-20 15:45:07 +0200218 FieldTrialStructMember("fps", [](Config* c) { return &c->fps; }),
Åsa Persson1b247f12019-08-14 17:26:39 +0200219 FieldTrialStructMember("kbps", [](Config* c) { return &c->kbps; }),
Åsa Persson12314192019-06-20 15:45:07 +0200220 FieldTrialStructMember("vp8_qp_low",
Åsa Persson48284b82019-07-08 10:01:12 +0200221 [](Config* c) { return &c->vp8.qp_low; }),
Åsa Persson12314192019-06-20 15:45:07 +0200222 FieldTrialStructMember("vp8_qp_high",
Åsa Persson48284b82019-07-08 10:01:12 +0200223 [](Config* c) { return &c->vp8.qp_high; }),
224 FieldTrialStructMember("vp8_fps", [](Config* c) { return &c->vp8.fps; }),
Åsa Persson12314192019-06-20 15:45:07 +0200225 FieldTrialStructMember("vp9_qp_low",
Åsa Persson48284b82019-07-08 10:01:12 +0200226 [](Config* c) { return &c->vp9.qp_low; }),
Åsa Persson12314192019-06-20 15:45:07 +0200227 FieldTrialStructMember("vp9_qp_high",
Åsa Persson48284b82019-07-08 10:01:12 +0200228 [](Config* c) { return &c->vp9.qp_high; }),
229 FieldTrialStructMember("vp9_fps", [](Config* c) { return &c->vp9.fps; }),
Åsa Persson12314192019-06-20 15:45:07 +0200230 FieldTrialStructMember("h264_qp_low",
Åsa Persson48284b82019-07-08 10:01:12 +0200231 [](Config* c) { return &c->h264.qp_low; }),
Åsa Persson12314192019-06-20 15:45:07 +0200232 FieldTrialStructMember("h264_qp_high",
Åsa Persson48284b82019-07-08 10:01:12 +0200233 [](Config* c) { return &c->h264.qp_high; }),
234 FieldTrialStructMember("h264_fps",
235 [](Config* c) { return &c->h264.fps; }),
Åsa Persson12314192019-06-20 15:45:07 +0200236 FieldTrialStructMember("generic_qp_low",
Åsa Persson48284b82019-07-08 10:01:12 +0200237 [](Config* c) { return &c->generic.qp_low; }),
Åsa Persson12314192019-06-20 15:45:07 +0200238 FieldTrialStructMember("generic_qp_high",
Åsa Persson48284b82019-07-08 10:01:12 +0200239 [](Config* c) { return &c->generic.qp_high; }),
240 FieldTrialStructMember("generic_fps",
241 [](Config* c) { return &c->generic.fps; })},
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200242 {});
243
244 ParseFieldTrial({&configs}, field_trial::FindFullName(kFieldTrial));
245
246 configs_ = GetValidOrDefault(configs.Get());
247 RTC_DCHECK_GT(configs_.size(), 1);
248}
249
250BalancedDegradationSettings::~BalancedDegradationSettings() {}
251
252std::vector<BalancedDegradationSettings::Config>
253BalancedDegradationSettings::GetConfigs() const {
254 return configs_;
255}
256
Åsa Persson48284b82019-07-08 10:01:12 +0200257int BalancedDegradationSettings::MinFps(VideoCodecType type, int pixels) const {
258 return GetFps(type, GetMinFpsConfig(pixels));
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200259}
260
Åsa Persson48284b82019-07-08 10:01:12 +0200261absl::optional<BalancedDegradationSettings::Config>
262BalancedDegradationSettings::GetMinFpsConfig(int pixels) const {
263 for (const auto& config : configs_) {
264 if (pixels <= config.pixels)
265 return config;
266 }
267 return absl::nullopt;
268}
269
270int BalancedDegradationSettings::MaxFps(VideoCodecType type, int pixels) const {
271 return GetFps(type, GetMaxFpsConfig(pixels));
272}
273
274absl::optional<BalancedDegradationSettings::Config>
275BalancedDegradationSettings::GetMaxFpsConfig(int pixels) const {
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200276 for (size_t i = 0; i < configs_.size() - 1; ++i) {
277 if (pixels <= configs_[i].pixels)
Åsa Persson48284b82019-07-08 10:01:12 +0200278 return configs_[i + 1];
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200279 }
Åsa Persson48284b82019-07-08 10:01:12 +0200280 return absl::nullopt;
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200281}
282
Åsa Persson1b247f12019-08-14 17:26:39 +0200283absl::optional<int> BalancedDegradationSettings::NextHigherBitrateKbps(
284 int pixels) const {
285 for (size_t i = 0; i < configs_.size() - 1; ++i) {
286 if (pixels <= configs_[i].pixels) {
287 return (configs_[i + 1].kbps > 0)
288 ? absl::optional<int>(configs_[i + 1].kbps)
289 : absl::nullopt;
290 }
291 }
292 return absl::nullopt;
293}
294
Åsa Persson12314192019-06-20 15:45:07 +0200295absl::optional<VideoEncoder::QpThresholds>
296BalancedDegradationSettings::GetQpThresholds(VideoCodecType type,
297 int pixels) const {
298 return GetThresholds(type, GetConfig(pixels));
299}
300
301BalancedDegradationSettings::Config BalancedDegradationSettings::GetConfig(
302 int pixels) const {
303 for (const auto& config : configs_) {
304 if (pixels <= config.pixels)
305 return config;
306 }
307 return configs_.back(); // Use last above highest pixels.
308}
309
Åsa Perssonf3d828e2019-05-06 12:22:49 +0200310} // namespace webrtc