xref: /aosp_15_r20/external/webrtc/video/config/simulcast.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
2  *  Copyright (c) 2014 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 "video/config/simulcast.h"
12 
13 #include <stdint.h>
14 #include <stdio.h>
15 
16 #include <algorithm>
17 #include <string>
18 #include <vector>
19 
20 #include "absl/strings/match.h"
21 #include "absl/types/optional.h"
22 #include "api/video/video_codec_constants.h"
23 #include "media/base/media_constants.h"
24 #include "modules/video_coding/utility/simulcast_rate_allocator.h"
25 #include "rtc_base/checks.h"
26 #include "rtc_base/experiments/field_trial_parser.h"
27 #include "rtc_base/experiments/min_video_bitrate_experiment.h"
28 #include "rtc_base/experiments/normalize_simulcast_size_experiment.h"
29 #include "rtc_base/experiments/rate_control_settings.h"
30 #include "rtc_base/logging.h"
31 
32 namespace cricket {
33 
34 namespace {
35 
36 constexpr char kUseLegacySimulcastLayerLimitFieldTrial[] =
37     "WebRTC-LegacySimulcastLayerLimit";
38 
39 constexpr double kDefaultMaxRoundupRate = 0.1;
40 
41 // Limits for legacy conference screensharing mode. Currently used for the
42 // lower of the two simulcast streams.
43 constexpr webrtc::DataRate kScreenshareDefaultTl0Bitrate =
44     webrtc::DataRate::KilobitsPerSec(200);
45 constexpr webrtc::DataRate kScreenshareDefaultTl1Bitrate =
46     webrtc::DataRate::KilobitsPerSec(1000);
47 
48 // Min/max bitrate for the higher one of the two simulcast stream used for
49 // screen content.
50 constexpr webrtc::DataRate kScreenshareHighStreamMinBitrate =
51     webrtc::DataRate::KilobitsPerSec(600);
52 constexpr webrtc::DataRate kScreenshareHighStreamMaxBitrate =
53     webrtc::DataRate::KilobitsPerSec(1250);
54 
55 constexpr int kDefaultNumTemporalLayers = 3;
56 constexpr int kScreenshareMaxSimulcastLayers = 2;
57 constexpr int kScreenshareTemporalLayers = 2;
58 
59 struct SimulcastFormat {
60   int width;
61   int height;
62   // The maximum number of simulcast layers can be used for
63   // resolutions at `widthxheight` for legacy applications.
64   size_t max_layers;
65   // The maximum bitrate for encoding stream at `widthxheight`, when we are
66   // not sending the next higher spatial stream.
67   webrtc::DataRate max_bitrate;
68   // The target bitrate for encoding stream at `widthxheight`, when this layer
69   // is not the highest layer (i.e., when we are sending another higher spatial
70   // stream).
71   webrtc::DataRate target_bitrate;
72   // The minimum bitrate needed for encoding stream at `widthxheight`.
73   webrtc::DataRate min_bitrate;
74 };
75 
76 // These tables describe from which resolution we can use how many
77 // simulcast layers at what bitrates (maximum, target, and minimum).
78 // Important!! Keep this table from high resolution to low resolution.
79 constexpr const SimulcastFormat kSimulcastFormats[] = {
80     {1920, 1080, 3, webrtc::DataRate::KilobitsPerSec(5000),
81      webrtc::DataRate::KilobitsPerSec(4000),
82      webrtc::DataRate::KilobitsPerSec(800)},
83     {1280, 720, 3, webrtc::DataRate::KilobitsPerSec(2500),
84      webrtc::DataRate::KilobitsPerSec(2500),
85      webrtc::DataRate::KilobitsPerSec(600)},
86     {960, 540, 3, webrtc::DataRate::KilobitsPerSec(1200),
87      webrtc::DataRate::KilobitsPerSec(1200),
88      webrtc::DataRate::KilobitsPerSec(350)},
89     {640, 360, 2, webrtc::DataRate::KilobitsPerSec(700),
90      webrtc::DataRate::KilobitsPerSec(500),
91      webrtc::DataRate::KilobitsPerSec(150)},
92     {480, 270, 2, webrtc::DataRate::KilobitsPerSec(450),
93      webrtc::DataRate::KilobitsPerSec(350),
94      webrtc::DataRate::KilobitsPerSec(150)},
95     {320, 180, 1, webrtc::DataRate::KilobitsPerSec(200),
96      webrtc::DataRate::KilobitsPerSec(150),
97      webrtc::DataRate::KilobitsPerSec(30)},
98     // As the resolution goes down, interpolate the target and max bitrates down
99     // towards zero. The min bitrate is still limited at 30 kbps and the target
100     // and the max will be capped from below accordingly.
101     {0, 0, 1, webrtc::DataRate::KilobitsPerSec(0),
102      webrtc::DataRate::KilobitsPerSec(0),
103      webrtc::DataRate::KilobitsPerSec(30)}};
104 
Interpolate(const webrtc::DataRate & a,const webrtc::DataRate & b,float rate)105 constexpr webrtc::DataRate Interpolate(const webrtc::DataRate& a,
106                                        const webrtc::DataRate& b,
107                                        float rate) {
108   return a * (1.0 - rate) + b * rate;
109 }
110 
111 // TODO(webrtc:12415): Flip this to a kill switch when this feature launches.
EnableLowresBitrateInterpolation(const webrtc::FieldTrialsView & trials)112 bool EnableLowresBitrateInterpolation(const webrtc::FieldTrialsView& trials) {
113   return absl::StartsWith(
114       trials.Lookup("WebRTC-LowresSimulcastBitrateInterpolation"), "Enabled");
115 }
116 
GetSimulcastFormats(bool enable_lowres_bitrate_interpolation)117 std::vector<SimulcastFormat> GetSimulcastFormats(
118     bool enable_lowres_bitrate_interpolation) {
119   std::vector<SimulcastFormat> formats;
120   formats.insert(formats.begin(), std::begin(kSimulcastFormats),
121                  std::end(kSimulcastFormats));
122   if (!enable_lowres_bitrate_interpolation) {
123     RTC_CHECK_GE(formats.size(), 2u);
124     SimulcastFormat& format0x0 = formats[formats.size() - 1];
125     const SimulcastFormat& format_prev = formats[formats.size() - 2];
126     format0x0.max_bitrate = format_prev.max_bitrate;
127     format0x0.target_bitrate = format_prev.target_bitrate;
128     format0x0.min_bitrate = format_prev.min_bitrate;
129   }
130   return formats;
131 }
132 
133 // Multiway: Number of temporal layers for each simulcast stream.
DefaultNumberOfTemporalLayers(const webrtc::FieldTrialsView & trials)134 int DefaultNumberOfTemporalLayers(const webrtc::FieldTrialsView& trials) {
135   const std::string group_name =
136       trials.Lookup("WebRTC-VP8ConferenceTemporalLayers");
137   if (group_name.empty())
138     return kDefaultNumTemporalLayers;
139 
140   int num_temporal_layers = kDefaultNumTemporalLayers;
141   if (sscanf(group_name.c_str(), "%d", &num_temporal_layers) == 1 &&
142       num_temporal_layers > 0 &&
143       num_temporal_layers <= webrtc::kMaxTemporalStreams) {
144     return num_temporal_layers;
145   }
146 
147   RTC_LOG(LS_WARNING) << "Attempt to set number of temporal layers to "
148                          "incorrect value: "
149                       << group_name;
150 
151   return kDefaultNumTemporalLayers;
152 }
153 
FindSimulcastFormatIndex(int width,int height,bool enable_lowres_bitrate_interpolation)154 int FindSimulcastFormatIndex(int width,
155                              int height,
156                              bool enable_lowres_bitrate_interpolation) {
157   RTC_DCHECK_GE(width, 0);
158   RTC_DCHECK_GE(height, 0);
159   const auto formats = GetSimulcastFormats(enable_lowres_bitrate_interpolation);
160   for (uint32_t i = 0; i < formats.size(); ++i) {
161     if (width * height >= formats[i].width * formats[i].height) {
162       return i;
163     }
164   }
165   RTC_DCHECK_NOTREACHED();
166   return -1;
167 }
168 
169 }  // namespace
170 
171 // Round size to nearest simulcast-friendly size.
172 // Simulcast stream width and height must both be dividable by
173 // |2 ^ (simulcast_layers - 1)|.
NormalizeSimulcastSize(int size,size_t simulcast_layers)174 int NormalizeSimulcastSize(int size, size_t simulcast_layers) {
175   int base2_exponent = static_cast<int>(simulcast_layers) - 1;
176   const absl::optional<int> experimental_base2_exponent =
177       webrtc::NormalizeSimulcastSizeExperiment::GetBase2Exponent();
178   if (experimental_base2_exponent &&
179       (size > (1 << *experimental_base2_exponent))) {
180     base2_exponent = *experimental_base2_exponent;
181   }
182   return ((size >> base2_exponent) << base2_exponent);
183 }
184 
InterpolateSimulcastFormat(int width,int height,absl::optional<double> max_roundup_rate,bool enable_lowres_bitrate_interpolation)185 SimulcastFormat InterpolateSimulcastFormat(
186     int width,
187     int height,
188     absl::optional<double> max_roundup_rate,
189     bool enable_lowres_bitrate_interpolation) {
190   const auto formats = GetSimulcastFormats(enable_lowres_bitrate_interpolation);
191   const int index = FindSimulcastFormatIndex(
192       width, height, enable_lowres_bitrate_interpolation);
193   if (index == 0)
194     return formats[index];
195   const int total_pixels_up =
196       formats[index - 1].width * formats[index - 1].height;
197   const int total_pixels_down = formats[index].width * formats[index].height;
198   const int total_pixels = width * height;
199   const float rate = (total_pixels_up - total_pixels) /
200                      static_cast<float>(total_pixels_up - total_pixels_down);
201 
202   // Use upper resolution if `rate` is below the configured threshold.
203   size_t max_layers = (rate < max_roundup_rate.value_or(kDefaultMaxRoundupRate))
204                           ? formats[index - 1].max_layers
205                           : formats[index].max_layers;
206   webrtc::DataRate max_bitrate = Interpolate(formats[index - 1].max_bitrate,
207                                              formats[index].max_bitrate, rate);
208   webrtc::DataRate target_bitrate = Interpolate(
209       formats[index - 1].target_bitrate, formats[index].target_bitrate, rate);
210   webrtc::DataRate min_bitrate = Interpolate(formats[index - 1].min_bitrate,
211                                              formats[index].min_bitrate, rate);
212 
213   return {width, height, max_layers, max_bitrate, target_bitrate, min_bitrate};
214 }
215 
InterpolateSimulcastFormat(int width,int height,bool enable_lowres_bitrate_interpolation)216 SimulcastFormat InterpolateSimulcastFormat(
217     int width,
218     int height,
219     bool enable_lowres_bitrate_interpolation) {
220   return InterpolateSimulcastFormat(width, height, absl::nullopt,
221                                     enable_lowres_bitrate_interpolation);
222 }
223 
FindSimulcastMaxBitrate(int width,int height,bool enable_lowres_bitrate_interpolation)224 webrtc::DataRate FindSimulcastMaxBitrate(
225     int width,
226     int height,
227     bool enable_lowres_bitrate_interpolation) {
228   return InterpolateSimulcastFormat(width, height,
229                                     enable_lowres_bitrate_interpolation)
230       .max_bitrate;
231 }
232 
FindSimulcastTargetBitrate(int width,int height,bool enable_lowres_bitrate_interpolation)233 webrtc::DataRate FindSimulcastTargetBitrate(
234     int width,
235     int height,
236     bool enable_lowres_bitrate_interpolation) {
237   return InterpolateSimulcastFormat(width, height,
238                                     enable_lowres_bitrate_interpolation)
239       .target_bitrate;
240 }
241 
FindSimulcastMinBitrate(int width,int height,bool enable_lowres_bitrate_interpolation)242 webrtc::DataRate FindSimulcastMinBitrate(
243     int width,
244     int height,
245     bool enable_lowres_bitrate_interpolation) {
246   return InterpolateSimulcastFormat(width, height,
247                                     enable_lowres_bitrate_interpolation)
248       .min_bitrate;
249 }
250 
BoostMaxSimulcastLayer(webrtc::DataRate max_bitrate,std::vector<webrtc::VideoStream> * layers)251 void BoostMaxSimulcastLayer(webrtc::DataRate max_bitrate,
252                             std::vector<webrtc::VideoStream>* layers) {
253   if (layers->empty())
254     return;
255 
256   const webrtc::DataRate total_bitrate = GetTotalMaxBitrate(*layers);
257 
258   // We're still not using all available bits.
259   if (total_bitrate < max_bitrate) {
260     // Spend additional bits to boost the max layer.
261     const webrtc::DataRate bitrate_left = max_bitrate - total_bitrate;
262     layers->back().max_bitrate_bps += bitrate_left.bps();
263   }
264 }
265 
GetTotalMaxBitrate(const std::vector<webrtc::VideoStream> & layers)266 webrtc::DataRate GetTotalMaxBitrate(
267     const std::vector<webrtc::VideoStream>& layers) {
268   if (layers.empty())
269     return webrtc::DataRate::Zero();
270 
271   int total_max_bitrate_bps = 0;
272   for (size_t s = 0; s < layers.size() - 1; ++s) {
273     total_max_bitrate_bps += layers[s].target_bitrate_bps;
274   }
275   total_max_bitrate_bps += layers.back().max_bitrate_bps;
276   return webrtc::DataRate::BitsPerSec(total_max_bitrate_bps);
277 }
278 
LimitSimulcastLayerCount(int width,int height,size_t need_layers,size_t layer_count,const webrtc::FieldTrialsView & trials)279 size_t LimitSimulcastLayerCount(int width,
280                                 int height,
281                                 size_t need_layers,
282                                 size_t layer_count,
283                                 const webrtc::FieldTrialsView& trials) {
284   if (!absl::StartsWith(trials.Lookup(kUseLegacySimulcastLayerLimitFieldTrial),
285                         "Disabled")) {
286     // Max layers from one higher resolution in kSimulcastFormats will be used
287     // if the ratio (pixels_up - pixels) / (pixels_up - pixels_down) is less
288     // than configured `max_ratio`. pixels_down is the selected index in
289     // kSimulcastFormats based on pixels.
290     webrtc::FieldTrialOptional<double> max_ratio("max_ratio");
291     webrtc::ParseFieldTrial({&max_ratio},
292                             trials.Lookup("WebRTC-SimulcastLayerLimitRoundUp"));
293 
294     const bool enable_lowres_bitrate_interpolation =
295         EnableLowresBitrateInterpolation(trials);
296     size_t adaptive_layer_count = std::max(
297         need_layers,
298         InterpolateSimulcastFormat(width, height, max_ratio.GetOptional(),
299                                    enable_lowres_bitrate_interpolation)
300             .max_layers);
301     if (layer_count > adaptive_layer_count) {
302       RTC_LOG(LS_WARNING) << "Reducing simulcast layer count from "
303                           << layer_count << " to " << adaptive_layer_count;
304       layer_count = adaptive_layer_count;
305     }
306   }
307   return layer_count;
308 }
309 
GetSimulcastConfig(size_t min_layers,size_t max_layers,int width,int height,double bitrate_priority,int max_qp,bool is_screenshare_with_conference_mode,bool temporal_layers_supported,const webrtc::FieldTrialsView & trials)310 std::vector<webrtc::VideoStream> GetSimulcastConfig(
311     size_t min_layers,
312     size_t max_layers,
313     int width,
314     int height,
315     double bitrate_priority,
316     int max_qp,
317     bool is_screenshare_with_conference_mode,
318     bool temporal_layers_supported,
319     const webrtc::FieldTrialsView& trials) {
320   RTC_DCHECK_LE(min_layers, max_layers);
321   RTC_DCHECK(max_layers > 1 || is_screenshare_with_conference_mode);
322 
323   const bool base_heavy_tl3_rate_alloc =
324       webrtc::RateControlSettings::ParseFromKeyValueConfig(&trials)
325           .Vp8BaseHeavyTl3RateAllocation();
326   if (is_screenshare_with_conference_mode) {
327     return GetScreenshareLayers(max_layers, width, height, bitrate_priority,
328                                 max_qp, temporal_layers_supported,
329                                 base_heavy_tl3_rate_alloc, trials);
330   } else {
331     // Some applications rely on the old behavior limiting the simulcast layer
332     // count based on the resolution automatically, which they can get through
333     // the WebRTC-LegacySimulcastLayerLimit field trial until they update.
334     max_layers =
335         LimitSimulcastLayerCount(width, height, min_layers, max_layers, trials);
336 
337     return GetNormalSimulcastLayers(max_layers, width, height, bitrate_priority,
338                                     max_qp, temporal_layers_supported,
339                                     base_heavy_tl3_rate_alloc, trials);
340   }
341 }
342 
GetNormalSimulcastLayers(size_t layer_count,int width,int height,double bitrate_priority,int max_qp,bool temporal_layers_supported,bool base_heavy_tl3_rate_alloc,const webrtc::FieldTrialsView & trials)343 std::vector<webrtc::VideoStream> GetNormalSimulcastLayers(
344     size_t layer_count,
345     int width,
346     int height,
347     double bitrate_priority,
348     int max_qp,
349     bool temporal_layers_supported,
350     bool base_heavy_tl3_rate_alloc,
351     const webrtc::FieldTrialsView& trials) {
352   std::vector<webrtc::VideoStream> layers(layer_count);
353 
354   const bool enable_lowres_bitrate_interpolation =
355       EnableLowresBitrateInterpolation(trials);
356 
357   // Format width and height has to be divisible by |2 ^ num_simulcast_layers -
358   // 1|.
359   width = NormalizeSimulcastSize(width, layer_count);
360   height = NormalizeSimulcastSize(height, layer_count);
361   // Add simulcast streams, from highest resolution (`s` = num_simulcast_layers
362   // -1) to lowest resolution at `s` = 0.
363   for (size_t s = layer_count - 1;; --s) {
364     layers[s].width = width;
365     layers[s].height = height;
366     // TODO(pbos): Fill actual temporal-layer bitrate thresholds.
367     layers[s].max_qp = max_qp;
368     layers[s].num_temporal_layers =
369         temporal_layers_supported ? DefaultNumberOfTemporalLayers(trials) : 1;
370     layers[s].max_bitrate_bps =
371         FindSimulcastMaxBitrate(width, height,
372                                 enable_lowres_bitrate_interpolation)
373             .bps();
374     layers[s].target_bitrate_bps =
375         FindSimulcastTargetBitrate(width, height,
376                                    enable_lowres_bitrate_interpolation)
377             .bps();
378     int num_temporal_layers = DefaultNumberOfTemporalLayers(trials);
379     if (s == 0) {
380       // If alternative temporal rate allocation is selected, adjust the
381       // bitrate of the lowest simulcast stream so that absolute bitrate for
382       // the base temporal layer matches the bitrate for the base temporal
383       // layer with the default 3 simulcast streams. Otherwise we risk a
384       // higher threshold for receiving a feed at all.
385       float rate_factor = 1.0;
386       if (num_temporal_layers == 3) {
387         if (base_heavy_tl3_rate_alloc) {
388           // Base heavy allocation increases TL0 bitrate from 40% to 60%.
389           rate_factor = 0.4 / 0.6;
390         }
391       } else {
392         rate_factor =
393             webrtc::SimulcastRateAllocator::GetTemporalRateAllocation(
394                 3, 0, /*base_heavy_tl3_rate_alloc=*/false) /
395             webrtc::SimulcastRateAllocator::GetTemporalRateAllocation(
396                 num_temporal_layers, 0, /*base_heavy_tl3_rate_alloc=*/false);
397       }
398 
399       layers[s].max_bitrate_bps =
400           static_cast<int>(layers[s].max_bitrate_bps * rate_factor);
401       layers[s].target_bitrate_bps =
402           static_cast<int>(layers[s].target_bitrate_bps * rate_factor);
403     }
404     layers[s].min_bitrate_bps =
405         FindSimulcastMinBitrate(width, height,
406                                 enable_lowres_bitrate_interpolation)
407             .bps();
408 
409     // Ensure consistency.
410     layers[s].max_bitrate_bps =
411         std::max(layers[s].min_bitrate_bps, layers[s].max_bitrate_bps);
412     layers[s].target_bitrate_bps =
413         std::max(layers[s].min_bitrate_bps, layers[s].target_bitrate_bps);
414 
415     layers[s].max_framerate = kDefaultVideoMaxFramerate;
416 
417     width /= 2;
418     height /= 2;
419 
420     if (s == 0) {
421       break;
422     }
423   }
424   // Currently the relative bitrate priority of the sender is controlled by
425   // the value of the lowest VideoStream.
426   // TODO(bugs.webrtc.org/8630): The web specification describes being able to
427   // control relative bitrate for each individual simulcast layer, but this
428   // is currently just implemented per rtp sender.
429   layers[0].bitrate_priority = bitrate_priority;
430   return layers;
431 }
432 
GetScreenshareLayers(size_t max_layers,int width,int height,double bitrate_priority,int max_qp,bool temporal_layers_supported,bool base_heavy_tl3_rate_alloc,const webrtc::FieldTrialsView & trials)433 std::vector<webrtc::VideoStream> GetScreenshareLayers(
434     size_t max_layers,
435     int width,
436     int height,
437     double bitrate_priority,
438     int max_qp,
439     bool temporal_layers_supported,
440     bool base_heavy_tl3_rate_alloc,
441     const webrtc::FieldTrialsView& trials) {
442   size_t num_simulcast_layers =
443       std::min<int>(max_layers, kScreenshareMaxSimulcastLayers);
444 
445   std::vector<webrtc::VideoStream> layers(num_simulcast_layers);
446   // For legacy screenshare in conference mode, tl0 and tl1 bitrates are
447   // piggybacked on the VideoCodec struct as target and max bitrates,
448   // respectively. See eg. webrtc::LibvpxVp8Encoder::SetRates().
449   layers[0].width = width;
450   layers[0].height = height;
451   layers[0].max_qp = max_qp;
452   layers[0].max_framerate = 5;
453   layers[0].min_bitrate_bps = webrtc::kDefaultMinVideoBitrateBps;
454   layers[0].target_bitrate_bps = kScreenshareDefaultTl0Bitrate.bps();
455   layers[0].max_bitrate_bps = kScreenshareDefaultTl1Bitrate.bps();
456   layers[0].num_temporal_layers = temporal_layers_supported ? 2 : 1;
457 
458   // With simulcast enabled, add another spatial layer. This one will have a
459   // more normal layout, with the regular 3 temporal layer pattern and no fps
460   // restrictions. The base simulcast layer will still use legacy setup.
461   if (num_simulcast_layers == kScreenshareMaxSimulcastLayers) {
462     // Add optional upper simulcast layer.
463     int max_bitrate_bps;
464     bool using_boosted_bitrate = false;
465     if (!temporal_layers_supported) {
466       // Set the max bitrate to where the base layer would have been if temporal
467       // layers were enabled.
468       max_bitrate_bps = static_cast<int>(
469           kScreenshareHighStreamMaxBitrate.bps() *
470           webrtc::SimulcastRateAllocator::GetTemporalRateAllocation(
471               kScreenshareTemporalLayers, 0, base_heavy_tl3_rate_alloc));
472     } else {
473       // Experimental temporal layer mode used, use increased max bitrate.
474       max_bitrate_bps = kScreenshareHighStreamMaxBitrate.bps();
475       using_boosted_bitrate = true;
476     }
477 
478     layers[1].width = width;
479     layers[1].height = height;
480     layers[1].max_qp = max_qp;
481     layers[1].max_framerate = kDefaultVideoMaxFramerate;
482     layers[1].num_temporal_layers =
483         temporal_layers_supported ? kScreenshareTemporalLayers : 1;
484     layers[1].min_bitrate_bps = using_boosted_bitrate
485                                     ? kScreenshareHighStreamMinBitrate.bps()
486                                     : layers[0].target_bitrate_bps * 2;
487     layers[1].target_bitrate_bps = max_bitrate_bps;
488     layers[1].max_bitrate_bps = max_bitrate_bps;
489   }
490 
491   // The bitrate priority currently implemented on a per-sender level, so we
492   // just set it for the first simulcast layer.
493   layers[0].bitrate_priority = bitrate_priority;
494   return layers;
495 }
496 
497 }  // namespace cricket
498