xref: /aosp_15_r20/external/webrtc/video/config/simulcast.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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