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