1 /*
2 * Copyright 2018 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 "media/engine/simulcast.h"
12
13 #include "media/base/media_constants.h"
14 #include "media/engine/constants.h"
15 #include "test/field_trial.h"
16 #include "test/gtest.h"
17
18 namespace webrtc {
19 namespace {
20 constexpr int kQpMax = 55;
21 constexpr double kBitratePriority = 2.0;
22 constexpr bool kScreenshare = true;
23 constexpr int kDefaultTemporalLayers = 3; // Value from simulcast.cc.
24
25 // Values from kSimulcastConfigs in simulcast.cc.
GetSimulcastBitrates720p()26 const std::vector<VideoStream> GetSimulcastBitrates720p() {
27 std::vector<VideoStream> streams(3);
28 streams[0].min_bitrate_bps = 30000;
29 streams[0].target_bitrate_bps = 150000;
30 streams[0].max_bitrate_bps = 200000;
31 streams[1].min_bitrate_bps = 150000;
32 streams[1].target_bitrate_bps = 500000;
33 streams[1].max_bitrate_bps = 700000;
34 streams[2].min_bitrate_bps = 600000;
35 streams[2].target_bitrate_bps = 2500000;
36 streams[2].max_bitrate_bps = 2500000;
37 return streams;
38 }
39 } // namespace
40
TEST(SimulcastTest,TotalMaxBitrateIsZeroForNoStreams)41 TEST(SimulcastTest, TotalMaxBitrateIsZeroForNoStreams) {
42 std::vector<VideoStream> streams;
43 EXPECT_EQ(0, cricket::GetTotalMaxBitrate(streams).bps());
44 }
45
TEST(SimulcastTest,GetTotalMaxBitrateForSingleStream)46 TEST(SimulcastTest, GetTotalMaxBitrateForSingleStream) {
47 std::vector<VideoStream> streams(1);
48 streams[0].max_bitrate_bps = 100000;
49 EXPECT_EQ(100000, cricket::GetTotalMaxBitrate(streams).bps());
50 }
51
TEST(SimulcastTest,GetTotalMaxBitrateForMultipleStreams)52 TEST(SimulcastTest, GetTotalMaxBitrateForMultipleStreams) {
53 std::vector<VideoStream> streams(3);
54 streams[0].target_bitrate_bps = 100000;
55 streams[1].target_bitrate_bps = 200000;
56 streams[2].max_bitrate_bps = 400000;
57 EXPECT_EQ(700000, cricket::GetTotalMaxBitrate(streams).bps());
58 }
59
TEST(SimulcastTest,BandwidthAboveTotalMaxBitrateGivenToHighestStream)60 TEST(SimulcastTest, BandwidthAboveTotalMaxBitrateGivenToHighestStream) {
61 std::vector<VideoStream> streams(3);
62 streams[0].target_bitrate_bps = 100000;
63 streams[1].target_bitrate_bps = 200000;
64 streams[2].max_bitrate_bps = 400000;
65
66 const webrtc::DataRate one_bps = webrtc::DataRate::BitsPerSec(1);
67
68 // No bitrate above the total max to give to the highest stream.
69 const webrtc::DataRate max_total_bitrate =
70 cricket::GetTotalMaxBitrate(streams);
71 cricket::BoostMaxSimulcastLayer(max_total_bitrate, &streams);
72 EXPECT_EQ(400000, streams[2].max_bitrate_bps);
73 EXPECT_EQ(max_total_bitrate, cricket::GetTotalMaxBitrate(streams));
74
75 // The bitrate above the total max should be given to the highest stream.
76 cricket::BoostMaxSimulcastLayer(max_total_bitrate + one_bps, &streams);
77 EXPECT_EQ(400000 + 1, streams[2].max_bitrate_bps);
78 EXPECT_EQ(max_total_bitrate + one_bps, cricket::GetTotalMaxBitrate(streams));
79 }
80
TEST(SimulcastTest,GetConfig)81 TEST(SimulcastTest, GetConfig) {
82 const std::vector<VideoStream> kExpected = GetSimulcastBitrates720p();
83
84 const size_t kMinLayers = 1;
85 const size_t kMaxLayers = 3;
86 std::vector<VideoStream> streams = cricket::GetSimulcastConfig(
87 kMinLayers, kMaxLayers, 1280, 720, kBitratePriority, kQpMax,
88 !kScreenshare, true);
89
90 EXPECT_EQ(kMaxLayers, streams.size());
91 EXPECT_EQ(320u, streams[0].width);
92 EXPECT_EQ(180u, streams[0].height);
93 EXPECT_EQ(640u, streams[1].width);
94 EXPECT_EQ(360u, streams[1].height);
95 EXPECT_EQ(1280u, streams[2].width);
96 EXPECT_EQ(720u, streams[2].height);
97
98 for (size_t i = 0; i < streams.size(); ++i) {
99 EXPECT_EQ(size_t{kDefaultTemporalLayers}, streams[i].num_temporal_layers);
100 EXPECT_EQ(cricket::kDefaultVideoMaxFramerate, streams[i].max_framerate);
101 EXPECT_EQ(kQpMax, streams[i].max_qp);
102 EXPECT_EQ(kExpected[i].min_bitrate_bps, streams[i].min_bitrate_bps);
103 EXPECT_EQ(kExpected[i].target_bitrate_bps, streams[i].target_bitrate_bps);
104 EXPECT_EQ(kExpected[i].max_bitrate_bps, streams[i].max_bitrate_bps);
105 EXPECT_TRUE(streams[i].active);
106 }
107 // Currently set on lowest stream.
108 EXPECT_EQ(kBitratePriority, streams[0].bitrate_priority);
109 EXPECT_FALSE(streams[1].bitrate_priority);
110 EXPECT_FALSE(streams[2].bitrate_priority);
111 }
112
TEST(SimulcastTest,GetConfigWithBaseHeavyVP8TL3RateAllocation)113 TEST(SimulcastTest, GetConfigWithBaseHeavyVP8TL3RateAllocation) {
114 test::ScopedFieldTrials field_trials(
115 "WebRTC-UseBaseHeavyVP8TL3RateAllocation/Enabled/");
116
117 const std::vector<VideoStream> kExpected = GetSimulcastBitrates720p();
118
119 const size_t kMinLayers = 1;
120 const size_t kMaxLayers = 3;
121 std::vector<VideoStream> streams = cricket::GetSimulcastConfig(
122 kMinLayers, kMaxLayers, 1280, 720, kBitratePriority, kQpMax,
123 !kScreenshare, true);
124
125 EXPECT_EQ(kExpected[0].min_bitrate_bps, streams[0].min_bitrate_bps);
126 EXPECT_EQ(static_cast<int>(0.4 * kExpected[0].target_bitrate_bps / 0.6),
127 streams[0].target_bitrate_bps);
128 EXPECT_EQ(static_cast<int>(0.4 * kExpected[0].max_bitrate_bps / 0.6),
129 streams[0].max_bitrate_bps);
130 for (size_t i = 1; i < streams.size(); ++i) {
131 EXPECT_EQ(kExpected[i].min_bitrate_bps, streams[i].min_bitrate_bps);
132 EXPECT_EQ(kExpected[i].target_bitrate_bps, streams[i].target_bitrate_bps);
133 EXPECT_EQ(kExpected[i].max_bitrate_bps, streams[i].max_bitrate_bps);
134 }
135 }
136
TEST(SimulcastTest,GetConfigWithLimitedMaxLayers)137 TEST(SimulcastTest, GetConfigWithLimitedMaxLayers) {
138 const size_t kMinLayers = 1;
139 const size_t kMaxLayers = 2;
140 std::vector<VideoStream> streams = cricket::GetSimulcastConfig(
141 kMinLayers, kMaxLayers, 1280, 720, kBitratePriority, kQpMax,
142 !kScreenshare, true);
143
144 EXPECT_EQ(kMaxLayers, streams.size());
145 EXPECT_EQ(640u, streams[0].width);
146 EXPECT_EQ(360u, streams[0].height);
147 EXPECT_EQ(1280u, streams[1].width);
148 EXPECT_EQ(720u, streams[1].height);
149 }
150
TEST(SimulcastTest,GetConfigWithLimitedMaxLayersForResolution)151 TEST(SimulcastTest, GetConfigWithLimitedMaxLayersForResolution) {
152 test::ScopedFieldTrials field_trials(
153 "WebRTC-LegacySimulcastLayerLimit/Enabled/");
154 const size_t kMinLayers = 1;
155 const size_t kMaxLayers = 3;
156 std::vector<VideoStream> streams = cricket::GetSimulcastConfig(
157 kMinLayers, kMaxLayers, 800, 600, kBitratePriority, kQpMax, !kScreenshare,
158 true);
159
160 EXPECT_EQ(2u, streams.size());
161 EXPECT_EQ(400u, streams[0].width);
162 EXPECT_EQ(300u, streams[0].height);
163 EXPECT_EQ(800u, streams[1].width);
164 EXPECT_EQ(600u, streams[1].height);
165 }
166
TEST(SimulcastTest,GetConfigWithLowResolutionScreenshare)167 TEST(SimulcastTest, GetConfigWithLowResolutionScreenshare) {
168 test::ScopedFieldTrials field_trials(
169 "WebRTC-LegacySimulcastLayerLimit/Enabled/");
170 const size_t kMinLayers = 1;
171 const size_t kMaxLayers = 3;
172 std::vector<VideoStream> streams =
173 cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 100, 100,
174 kBitratePriority, kQpMax, kScreenshare, true);
175
176 // Simulcast streams number is never decreased for screenshare,
177 // even for very low resolution.
178 EXPECT_GT(streams.size(), 1u);
179 }
180
TEST(SimulcastTest,GetConfigWithNotLimitedMaxLayersForResolution)181 TEST(SimulcastTest, GetConfigWithNotLimitedMaxLayersForResolution) {
182 test::ScopedFieldTrials field_trials(
183 "WebRTC-LegacySimulcastLayerLimit/Disabled/");
184 const size_t kMinLayers = 1;
185 const size_t kMaxLayers = 3;
186 std::vector<VideoStream> streams = cricket::GetSimulcastConfig(
187 kMinLayers, kMaxLayers, 800, 600, kBitratePriority, kQpMax, !kScreenshare,
188 true);
189
190 EXPECT_EQ(kMaxLayers, streams.size());
191 EXPECT_EQ(200u, streams[0].width);
192 EXPECT_EQ(150u, streams[0].height);
193 EXPECT_EQ(400u, streams[1].width);
194 EXPECT_EQ(300u, streams[1].height);
195 EXPECT_EQ(800u, streams[2].width);
196 EXPECT_EQ(600u, streams[2].height);
197 }
198
TEST(SimulcastTest,GetConfigWithNormalizedResolution)199 TEST(SimulcastTest, GetConfigWithNormalizedResolution) {
200 const size_t kMinLayers = 1;
201 const size_t kMaxLayers = 2;
202 std::vector<VideoStream> streams = cricket::GetSimulcastConfig(
203 kMinLayers, kMaxLayers, 640 + 1, 360 + 1, kBitratePriority, kQpMax,
204 !kScreenshare, true);
205
206 // Must be divisible by |2 ^ (num_layers - 1)|.
207 EXPECT_EQ(kMaxLayers, streams.size());
208 EXPECT_EQ(320u, streams[0].width);
209 EXPECT_EQ(180u, streams[0].height);
210 EXPECT_EQ(640u, streams[1].width);
211 EXPECT_EQ(360u, streams[1].height);
212 }
213
TEST(SimulcastTest,GetConfigWithNormalizedResolutionDivisibleBy4)214 TEST(SimulcastTest, GetConfigWithNormalizedResolutionDivisibleBy4) {
215 test::ScopedFieldTrials field_trials(
216 "WebRTC-NormalizeSimulcastResolution/Enabled-2/");
217
218 const size_t kMinLayers = 1;
219 const size_t kMaxLayers = 2;
220 std::vector<VideoStream> streams = cricket::GetSimulcastConfig(
221 kMinLayers, kMaxLayers, 709, 501, kBitratePriority, kQpMax, !kScreenshare,
222 true);
223
224 // Must be divisible by |2 ^ 2|.
225 EXPECT_EQ(kMaxLayers, streams.size());
226 EXPECT_EQ(354u, streams[0].width);
227 EXPECT_EQ(250u, streams[0].height);
228 EXPECT_EQ(708u, streams[1].width);
229 EXPECT_EQ(500u, streams[1].height);
230 }
231
TEST(SimulcastTest,GetConfigWithNormalizedResolutionDivisibleBy8)232 TEST(SimulcastTest, GetConfigWithNormalizedResolutionDivisibleBy8) {
233 test::ScopedFieldTrials field_trials(
234 "WebRTC-NormalizeSimulcastResolution/Enabled-3/");
235
236 const size_t kMinLayers = 1;
237 const size_t kMaxLayers = 2;
238 std::vector<VideoStream> streams = cricket::GetSimulcastConfig(
239 kMinLayers, kMaxLayers, 709, 501, kBitratePriority, kQpMax, !kScreenshare,
240 true);
241
242 // Must be divisible by |2 ^ 3|.
243 EXPECT_EQ(kMaxLayers, streams.size());
244 EXPECT_EQ(352u, streams[0].width);
245 EXPECT_EQ(248u, streams[0].height);
246 EXPECT_EQ(704u, streams[1].width);
247 EXPECT_EQ(496u, streams[1].height);
248 }
249
TEST(SimulcastTest,GetConfigForLegacyLayerLimit)250 TEST(SimulcastTest, GetConfigForLegacyLayerLimit) {
251 test::ScopedFieldTrials field_trials(
252 "WebRTC-LegacySimulcastLayerLimit/Enabled/");
253
254 const size_t kMinLayers = 1;
255 const int kMaxLayers = 3;
256 std::vector<VideoStream> streams = cricket::GetSimulcastConfig(
257 kMinLayers, kMaxLayers, 320, 180, kBitratePriority, kQpMax, !kScreenshare,
258 true);
259 EXPECT_EQ(1u, streams.size());
260
261 streams = cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 640, 360,
262 kBitratePriority, kQpMax, !kScreenshare,
263 true);
264 EXPECT_EQ(2u, streams.size());
265
266 streams = cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 1920, 1080,
267 kBitratePriority, kQpMax, !kScreenshare,
268 true);
269 EXPECT_EQ(3u, streams.size());
270 }
271
TEST(SimulcastTest,GetConfigForLegacyLayerLimitWithRequiredHD)272 TEST(SimulcastTest, GetConfigForLegacyLayerLimitWithRequiredHD) {
273 test::ScopedFieldTrials field_trials(
274 "WebRTC-LegacySimulcastLayerLimit/Enabled/");
275
276 const size_t kMinLayers = 3; // "HD" layer must be present!
277 const int kMaxLayers = 3;
278 std::vector<VideoStream> streams = cricket::GetSimulcastConfig(
279 kMinLayers, kMaxLayers, 320, 180, kBitratePriority, kQpMax, !kScreenshare,
280 true);
281 EXPECT_EQ(3u, streams.size());
282
283 streams = cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 640, 360,
284 kBitratePriority, kQpMax, !kScreenshare,
285 true);
286 EXPECT_EQ(3u, streams.size());
287
288 streams = cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 1920, 1080,
289 kBitratePriority, kQpMax, !kScreenshare,
290 true);
291 EXPECT_EQ(3u, streams.size());
292 }
293
TEST(SimulcastTest,GetConfigForScreenshareSimulcast)294 TEST(SimulcastTest, GetConfigForScreenshareSimulcast) {
295 const size_t kMinLayers = 1;
296 const size_t kMaxLayers = 3;
297 std::vector<VideoStream> streams =
298 cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 1400, 800,
299 kBitratePriority, kQpMax, kScreenshare, true);
300
301 EXPECT_GT(streams.size(), 1u);
302 for (size_t i = 0; i < streams.size(); ++i) {
303 EXPECT_EQ(1400u, streams[i].width) << "Screen content never scaled.";
304 EXPECT_EQ(800u, streams[i].height) << "Screen content never scaled.";
305 EXPECT_EQ(kQpMax, streams[i].max_qp);
306 EXPECT_TRUE(streams[i].active);
307 EXPECT_GT(streams[i].num_temporal_layers, size_t{1});
308 EXPECT_GT(streams[i].max_framerate, 0);
309 EXPECT_GT(streams[i].min_bitrate_bps, 0);
310 EXPECT_GT(streams[i].target_bitrate_bps, streams[i].min_bitrate_bps);
311 EXPECT_GE(streams[i].max_bitrate_bps, streams[i].target_bitrate_bps);
312 }
313 }
314
TEST(SimulcastTest,GetConfigForScreenshareSimulcastWithLimitedMaxLayers)315 TEST(SimulcastTest, GetConfigForScreenshareSimulcastWithLimitedMaxLayers) {
316 const size_t kMinLayers = 1;
317 const size_t kMaxLayers = 1;
318 std::vector<VideoStream> streams =
319 cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 1400, 800,
320 kBitratePriority, kQpMax, kScreenshare, true);
321
322 EXPECT_EQ(kMaxLayers, streams.size());
323 }
324
TEST(SimulcastTest,SimulcastScreenshareMaxBitrateAdjustedForResolution)325 TEST(SimulcastTest, SimulcastScreenshareMaxBitrateAdjustedForResolution) {
326 constexpr int kScreenshareHighStreamMinBitrateBps = 600000;
327 constexpr int kScreenshareHighStreamMaxBitrateBps = 1250000;
328 constexpr int kMaxBitrate960_540 = 1200000;
329
330 // Normal case, max bitrate not limited by resolution.
331 const size_t kMinLayers = 1;
332 const size_t kMaxLayers = 2;
333 std::vector<VideoStream> streams =
334 cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 1920, 1080,
335 kBitratePriority, kQpMax, kScreenshare, true);
336 EXPECT_EQ(kMaxLayers, streams.size());
337 EXPECT_EQ(streams[1].max_bitrate_bps, kScreenshareHighStreamMaxBitrateBps);
338 EXPECT_EQ(streams[1].min_bitrate_bps, kScreenshareHighStreamMinBitrateBps);
339 EXPECT_GE(streams[1].max_bitrate_bps, streams[1].min_bitrate_bps);
340
341 // At 960x540, the max bitrate is limited to 900kbps.
342 streams =
343 cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 960, 540,
344 kBitratePriority, kQpMax, kScreenshare, true);
345 EXPECT_EQ(kMaxLayers, streams.size());
346 EXPECT_EQ(streams[1].max_bitrate_bps, kMaxBitrate960_540);
347 EXPECT_EQ(streams[1].min_bitrate_bps, kScreenshareHighStreamMinBitrateBps);
348 EXPECT_GE(streams[1].max_bitrate_bps, streams[1].min_bitrate_bps);
349
350 // At 480x270, the max bitrate is limited to 450kbps. This is lower than
351 // the min bitrate, so use that as a lower bound.
352 streams =
353 cricket::GetSimulcastConfig(kMinLayers, kMaxLayers, 480, 270,
354 kBitratePriority, kQpMax, kScreenshare, true);
355 EXPECT_EQ(kMaxLayers, streams.size());
356 EXPECT_EQ(streams[1].max_bitrate_bps, kScreenshareHighStreamMinBitrateBps);
357 EXPECT_EQ(streams[1].min_bitrate_bps, kScreenshareHighStreamMinBitrateBps);
358 EXPECT_GE(streams[1].max_bitrate_bps, streams[1].min_bitrate_bps);
359 }
360
TEST(SimulcastTest,AveragesBitratesForNonStandardResolution)361 TEST(SimulcastTest, AveragesBitratesForNonStandardResolution) {
362 const size_t kMinLayers = 1;
363 const size_t kMaxLayers = 3;
364 std::vector<VideoStream> streams = cricket::GetSimulcastConfig(
365 kMinLayers, kMaxLayers, 900, 800, kBitratePriority, kQpMax, !kScreenshare,
366 true);
367
368 EXPECT_EQ(kMaxLayers, streams.size());
369 EXPECT_EQ(900u, streams[2].width);
370 EXPECT_EQ(800u, streams[2].height);
371 EXPECT_EQ(1850000, streams[2].max_bitrate_bps);
372 EXPECT_EQ(1850000, streams[2].target_bitrate_bps);
373 EXPECT_EQ(475000, streams[2].min_bitrate_bps);
374 }
375
TEST(SimulcastTest,BitratesForCloseToStandardResolution)376 TEST(SimulcastTest, BitratesForCloseToStandardResolution) {
377 const size_t kMinLayers = 1;
378 const size_t kMaxLayers = 3;
379 // Resolution very close to 720p in number of pixels
380 const size_t kWidth = 1280;
381 const size_t kHeight = 716;
382 const std::vector<VideoStream> kExpectedNear = GetSimulcastBitrates720p();
383
384 std::vector<VideoStream> streams = cricket::GetSimulcastConfig(
385 kMinLayers, kMaxLayers, kWidth, kHeight, kBitratePriority, kQpMax,
386 !kScreenshare, true);
387
388 EXPECT_EQ(kMaxLayers, streams.size());
389 EXPECT_EQ(kWidth, streams[2].width);
390 EXPECT_EQ(kHeight, streams[2].height);
391 for (size_t i = 0; i < streams.size(); ++i) {
392 EXPECT_NEAR(kExpectedNear[i].max_bitrate_bps, streams[i].max_bitrate_bps,
393 20000);
394 EXPECT_NEAR(kExpectedNear[i].target_bitrate_bps,
395 streams[i].target_bitrate_bps, 20000);
396 EXPECT_NEAR(kExpectedNear[i].min_bitrate_bps, streams[i].min_bitrate_bps,
397 20000);
398 }
399 }
400
401 } // namespace webrtc
402