• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
19 namespace webrtc {
20 namespace {
21 constexpr char kFieldTrial[] = "WebRTC-Video-BalancedDegradationSettings";
22 constexpr int kMinFps = 1;
23 constexpr int kMaxFps = 100;  // 100 means unlimited fps.
24 
DefaultConfigs()25 std::vector<BalancedDegradationSettings::Config> DefaultConfigs() {
26   return {{320 * 240,
27            7,
28            0,
29            0,
30            BalancedDegradationSettings::kNoFpsDiff,
31            {0, 0, 0, 0, 0},
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           {480 * 360,
37            10,
38            0,
39            0,
40            1,
41            {0, 0, 0, 0, 0},
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           {640 * 480,
47            15,
48            0,
49            0,
50            1,
51            {0, 0, 0, 0, 0},
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 }
57 
IsValidConfig(const BalancedDegradationSettings::CodecTypeSpecific & config)58 bool IsValidConfig(
59     const BalancedDegradationSettings::CodecTypeSpecific& config) {
60   if (config.GetQpLow().has_value() != config.GetQpHigh().has_value()) {
61     RTC_LOG(LS_WARNING) << "Neither or both thresholds should be set.";
62     return false;
63   }
64   if (config.GetQpLow().has_value() && config.GetQpHigh().has_value() &&
65       config.GetQpLow().value() >= config.GetQpHigh().value()) {
66     RTC_LOG(LS_WARNING) << "Invalid threshold value, low >= high threshold.";
67     return false;
68   }
69   if (config.GetFps().has_value() && (config.GetFps().value() < kMinFps ||
70                                       config.GetFps().value() > kMaxFps)) {
71     RTC_LOG(LS_WARNING) << "Unsupported fps setting, value ignored.";
72     return false;
73   }
74   return true;
75 }
76 
IsValid(const BalancedDegradationSettings::CodecTypeSpecific & config1,const BalancedDegradationSettings::CodecTypeSpecific & config2)77 bool IsValid(const BalancedDegradationSettings::CodecTypeSpecific& config1,
78              const BalancedDegradationSettings::CodecTypeSpecific& config2) {
79   bool both_or_none_set = ((config1.qp_low > 0) == (config2.qp_low > 0) &&
80                            (config1.qp_high > 0) == (config2.qp_high > 0) &&
81                            (config1.fps > 0) == (config2.fps > 0));
82   if (!both_or_none_set) {
83     RTC_LOG(LS_WARNING) << "Invalid value, all/none should be set.";
84     return false;
85   }
86   if (config1.fps > 0 && config1.fps < config2.fps) {
87     RTC_LOG(LS_WARNING) << "Invalid fps/pixel value provided.";
88     return false;
89   }
90   return true;
91 }
92 
IsValid(const std::vector<BalancedDegradationSettings::Config> & configs)93 bool IsValid(const std::vector<BalancedDegradationSettings::Config>& configs) {
94   if (configs.size() <= 1) {
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(const FieldTrialsView & field_trials)334 BalancedDegradationSettings::BalancedDegradationSettings(
335     const FieldTrialsView& field_trials) {
336   FieldTrialStructList<Config> configs(
337       {FieldTrialStructMember("pixels", [](Config* c) { return &c->pixels; }),
338        FieldTrialStructMember("fps", [](Config* c) { return &c->fps; }),
339        FieldTrialStructMember("kbps", [](Config* c) { return &c->kbps; }),
340        FieldTrialStructMember("kbps_res",
341                               [](Config* c) { return &c->kbps_res; }),
342        FieldTrialStructMember("fps_diff",
343                               [](Config* c) { return &c->fps_diff; }),
344        FieldTrialStructMember("vp8_qp_low",
345                               [](Config* c) { return &c->vp8.qp_low; }),
346        FieldTrialStructMember("vp8_qp_high",
347                               [](Config* c) { return &c->vp8.qp_high; }),
348        FieldTrialStructMember("vp8_fps", [](Config* c) { return &c->vp8.fps; }),
349        FieldTrialStructMember("vp8_kbps",
350                               [](Config* c) { return &c->vp8.kbps; }),
351        FieldTrialStructMember("vp8_kbps_res",
352                               [](Config* c) { return &c->vp8.kbps_res; }),
353        FieldTrialStructMember("vp9_qp_low",
354                               [](Config* c) { return &c->vp9.qp_low; }),
355        FieldTrialStructMember("vp9_qp_high",
356                               [](Config* c) { return &c->vp9.qp_high; }),
357        FieldTrialStructMember("vp9_fps", [](Config* c) { return &c->vp9.fps; }),
358        FieldTrialStructMember("vp9_kbps",
359                               [](Config* c) { return &c->vp9.kbps; }),
360        FieldTrialStructMember("vp9_kbps_res",
361                               [](Config* c) { return &c->vp9.kbps_res; }),
362        FieldTrialStructMember("h264_qp_low",
363                               [](Config* c) { return &c->h264.qp_low; }),
364        FieldTrialStructMember("h264_qp_high",
365                               [](Config* c) { return &c->h264.qp_high; }),
366        FieldTrialStructMember("h264_fps",
367                               [](Config* c) { return &c->h264.fps; }),
368        FieldTrialStructMember("h264_kbps",
369                               [](Config* c) { return &c->h264.kbps; }),
370        FieldTrialStructMember("h264_kbps_res",
371                               [](Config* c) { return &c->h264.kbps_res; }),
372        FieldTrialStructMember("av1_qp_low",
373                               [](Config* c) { return &c->av1.qp_low; }),
374        FieldTrialStructMember("av1_qp_high",
375                               [](Config* c) { return &c->av1.qp_high; }),
376        FieldTrialStructMember("av1_fps", [](Config* c) { return &c->av1.fps; }),
377        FieldTrialStructMember("av1_kbps",
378                               [](Config* c) { return &c->av1.kbps; }),
379        FieldTrialStructMember("av1_kbps_res",
380                               [](Config* c) { return &c->av1.kbps_res; }),
381        FieldTrialStructMember("generic_qp_low",
382                               [](Config* c) { return &c->generic.qp_low; }),
383        FieldTrialStructMember("generic_qp_high",
384                               [](Config* c) { return &c->generic.qp_high; }),
385        FieldTrialStructMember("generic_fps",
386                               [](Config* c) { return &c->generic.fps; }),
387        FieldTrialStructMember("generic_kbps",
388                               [](Config* c) { return &c->generic.kbps; }),
389        FieldTrialStructMember("generic_kbps_res",
390                               [](Config* c) { return &c->generic.kbps_res; })},
391       {});
392 
393   ParseFieldTrial({&configs}, field_trials.Lookup(kFieldTrial));
394 
395   configs_ = GetValidOrDefault(configs.Get());
396   RTC_DCHECK_GT(configs_.size(), 1);
397 }
398 
~BalancedDegradationSettings()399 BalancedDegradationSettings::~BalancedDegradationSettings() {}
400 
401 std::vector<BalancedDegradationSettings::Config>
GetConfigs() const402 BalancedDegradationSettings::GetConfigs() const {
403   return configs_;
404 }
405 
MinFps(VideoCodecType type,int pixels) const406 int BalancedDegradationSettings::MinFps(VideoCodecType type, int pixels) const {
407   return GetFps(type, GetMinFpsConfig(pixels));
408 }
409 
410 absl::optional<BalancedDegradationSettings::Config>
GetMinFpsConfig(int pixels) const411 BalancedDegradationSettings::GetMinFpsConfig(int pixels) const {
412   for (const auto& config : configs_) {
413     if (pixels <= config.pixels)
414       return config;
415   }
416   return absl::nullopt;
417 }
418 
MaxFps(VideoCodecType type,int pixels) const419 int BalancedDegradationSettings::MaxFps(VideoCodecType type, int pixels) const {
420   return GetFps(type, GetMaxFpsConfig(pixels));
421 }
422 
423 absl::optional<BalancedDegradationSettings::Config>
GetMaxFpsConfig(int pixels) const424 BalancedDegradationSettings::GetMaxFpsConfig(int pixels) const {
425   for (size_t i = 0; i < configs_.size() - 1; ++i) {
426     if (pixels <= configs_[i].pixels)
427       return configs_[i + 1];
428   }
429   return absl::nullopt;
430 }
431 
CanAdaptUp(VideoCodecType type,int pixels,uint32_t bitrate_bps) const432 bool BalancedDegradationSettings::CanAdaptUp(VideoCodecType type,
433                                              int pixels,
434                                              uint32_t bitrate_bps) const {
435   absl::optional<int> min_kbps = GetKbps(type, GetMaxFpsConfig(pixels));
436   if (!min_kbps.has_value() || bitrate_bps == 0) {
437     return true;  // No limit configured or bitrate provided.
438   }
439   return bitrate_bps >= static_cast<uint32_t>(min_kbps.value() * 1000);
440 }
441 
CanAdaptUpResolution(VideoCodecType type,int pixels,uint32_t bitrate_bps) const442 bool BalancedDegradationSettings::CanAdaptUpResolution(
443     VideoCodecType type,
444     int pixels,
445     uint32_t bitrate_bps) const {
446   absl::optional<int> min_kbps = GetKbpsRes(type, GetMaxFpsConfig(pixels));
447   if (!min_kbps.has_value() || bitrate_bps == 0) {
448     return true;  // No limit configured or bitrate provided.
449   }
450   return bitrate_bps >= static_cast<uint32_t>(min_kbps.value() * 1000);
451 }
452 
MinFpsDiff(int pixels) const453 absl::optional<int> BalancedDegradationSettings::MinFpsDiff(int pixels) const {
454   for (const auto& config : configs_) {
455     if (pixels <= config.pixels) {
456       return (config.fps_diff > kNoFpsDiff)
457                  ? absl::optional<int>(config.fps_diff)
458                  : absl::nullopt;
459     }
460   }
461   return absl::nullopt;
462 }
463 
464 absl::optional<VideoEncoder::QpThresholds>
GetQpThresholds(VideoCodecType type,int pixels) const465 BalancedDegradationSettings::GetQpThresholds(VideoCodecType type,
466                                              int pixels) const {
467   return GetThresholds(type, GetConfig(pixels));
468 }
469 
GetConfig(int pixels) const470 BalancedDegradationSettings::Config BalancedDegradationSettings::GetConfig(
471     int pixels) const {
472   for (const auto& config : configs_) {
473     if (pixels <= config.pixels)
474       return config;
475   }
476   return configs_.back();  // Use last above highest pixels.
477 }
478 
479 }  // namespace webrtc
480