blob: bb80c119300dcbbb731a851dd0305718444ea4e5 [file] [log] [blame]
perkj2d5f0912016-02-29 08:04:411/*
2 * Copyright 2016 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
Jonas Olssona4d87372019-07-05 17:08:3311#include "media/base/video_broadcaster.h"
12
Steve Antone78bcb92017-10-31 16:53:0813#include <limits>
14
Yves Gerey3e707812018-11-28 15:47:4915#include "absl/types/optional.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3116#include "api/video/i420_buffer.h"
17#include "api/video/video_frame.h"
Yves Gerey3e707812018-11-28 15:47:4918#include "api/video/video_rotation.h"
Markus Handell6fa9e682021-10-13 20:50:5319#include "api/video/video_source_interface.h"
Steve Anton10542f22019-01-11 17:11:0020#include "media/base/fake_video_renderer.h"
Markus Handell6fa9e682021-10-13 20:50:5321#include "test/gmock.h"
Yves Gerey3e707812018-11-28 15:47:4922#include "test/gtest.h"
perkj2d5f0912016-02-29 08:04:4123
Jonas Olssona4d87372019-07-05 17:08:3324using cricket::FakeVideoRenderer;
perkj2d5f0912016-02-29 08:04:4125using rtc::VideoBroadcaster;
26using rtc::VideoSinkWants;
Jonas Oreland0deda152022-09-23 10:08:5727using FrameSize = rtc::VideoSinkWants::FrameSize;
perkj2d5f0912016-02-29 08:04:4128
Markus Handell6fa9e682021-10-13 20:50:5329using ::testing::AllOf;
30using ::testing::Eq;
31using ::testing::Field;
32using ::testing::Mock;
33using ::testing::Optional;
34
35class MockSink : public rtc::VideoSinkInterface<webrtc::VideoFrame> {
36 public:
37 void OnFrame(const webrtc::VideoFrame&) override {}
38
39 MOCK_METHOD(void,
40 OnConstraintsChanged,
41 (const webrtc::VideoTrackSourceConstraints& constraints),
42 (override));
43};
44
perkj2d5f0912016-02-29 08:04:4145TEST(VideoBroadcasterTest, frame_wanted) {
46 VideoBroadcaster broadcaster;
47 EXPECT_FALSE(broadcaster.frame_wanted());
48
perkjd6c39542016-03-17 09:35:2349 FakeVideoRenderer sink;
perkj2d5f0912016-02-29 08:04:4150 broadcaster.AddOrUpdateSink(&sink, rtc::VideoSinkWants());
51 EXPECT_TRUE(broadcaster.frame_wanted());
52
53 broadcaster.RemoveSink(&sink);
54 EXPECT_FALSE(broadcaster.frame_wanted());
55}
56
57TEST(VideoBroadcasterTest, OnFrame) {
58 VideoBroadcaster broadcaster;
59
perkjd6c39542016-03-17 09:35:2360 FakeVideoRenderer sink1;
61 FakeVideoRenderer sink2;
perkj2d5f0912016-02-29 08:04:4162 broadcaster.AddOrUpdateSink(&sink1, rtc::VideoSinkWants());
63 broadcaster.AddOrUpdateSink(&sink2, rtc::VideoSinkWants());
nissedf2ceb82016-12-15 14:29:5364 static int kWidth = 100;
65 static int kHeight = 50;
perkj2d5f0912016-02-29 08:04:4166
nissedf2ceb82016-12-15 14:29:5367 rtc::scoped_refptr<webrtc::I420Buffer> buffer(
68 webrtc::I420Buffer::Create(kWidth, kHeight));
69 // Initialize, to avoid warnings on use of initialized values.
Niels Möllerba2de582022-04-20 14:46:2670 webrtc::I420Buffer::SetBlack(buffer.get());
nissedf2ceb82016-12-15 14:29:5371
Artem Titov1ebfb6a2019-01-03 22:49:3772 webrtc::VideoFrame frame = webrtc::VideoFrame::Builder()
73 .set_video_frame_buffer(buffer)
74 .set_rotation(webrtc::kVideoRotation_0)
75 .set_timestamp_us(0)
76 .build();
perkj2d5f0912016-02-29 08:04:4177
78 broadcaster.OnFrame(frame);
perkjd6c39542016-03-17 09:35:2379 EXPECT_EQ(1, sink1.num_rendered_frames());
80 EXPECT_EQ(1, sink2.num_rendered_frames());
perkj2d5f0912016-02-29 08:04:4181
82 broadcaster.RemoveSink(&sink1);
83 broadcaster.OnFrame(frame);
perkjd6c39542016-03-17 09:35:2384 EXPECT_EQ(1, sink1.num_rendered_frames());
85 EXPECT_EQ(2, sink2.num_rendered_frames());
perkj2d5f0912016-02-29 08:04:4186
87 broadcaster.AddOrUpdateSink(&sink1, rtc::VideoSinkWants());
88 broadcaster.OnFrame(frame);
perkjd6c39542016-03-17 09:35:2389 EXPECT_EQ(2, sink1.num_rendered_frames());
90 EXPECT_EQ(3, sink2.num_rendered_frames());
perkj2d5f0912016-02-29 08:04:4191}
92
93TEST(VideoBroadcasterTest, AppliesRotationIfAnySinkWantsRotationApplied) {
94 VideoBroadcaster broadcaster;
deadbeeff5629ad2016-03-18 18:38:2695 EXPECT_FALSE(broadcaster.wants().rotation_applied);
perkj2d5f0912016-02-29 08:04:4196
perkjd6c39542016-03-17 09:35:2397 FakeVideoRenderer sink1;
perkj2d5f0912016-02-29 08:04:4198 VideoSinkWants wants1;
99 wants1.rotation_applied = false;
100
101 broadcaster.AddOrUpdateSink(&sink1, wants1);
102 EXPECT_FALSE(broadcaster.wants().rotation_applied);
103
perkjd6c39542016-03-17 09:35:23104 FakeVideoRenderer sink2;
perkj2d5f0912016-02-29 08:04:41105 VideoSinkWants wants2;
106 wants2.rotation_applied = true;
107
108 broadcaster.AddOrUpdateSink(&sink2, wants2);
109 EXPECT_TRUE(broadcaster.wants().rotation_applied);
110
111 broadcaster.RemoveSink(&sink2);
112 EXPECT_FALSE(broadcaster.wants().rotation_applied);
113}
114
115TEST(VideoBroadcasterTest, AppliesMinOfSinkWantsMaxPixelCount) {
116 VideoBroadcaster broadcaster;
sprangc5d62e22017-04-03 06:53:04117 EXPECT_EQ(std::numeric_limits<int>::max(),
118 broadcaster.wants().max_pixel_count);
perkj2d5f0912016-02-29 08:04:41119
perkjd6c39542016-03-17 09:35:23120 FakeVideoRenderer sink1;
perkj2d5f0912016-02-29 08:04:41121 VideoSinkWants wants1;
sprangc5d62e22017-04-03 06:53:04122 wants1.max_pixel_count = 1280 * 720;
perkj2d5f0912016-02-29 08:04:41123
124 broadcaster.AddOrUpdateSink(&sink1, wants1);
sprangc5d62e22017-04-03 06:53:04125 EXPECT_EQ(1280 * 720, broadcaster.wants().max_pixel_count);
perkj2d5f0912016-02-29 08:04:41126
perkjd6c39542016-03-17 09:35:23127 FakeVideoRenderer sink2;
perkj2d5f0912016-02-29 08:04:41128 VideoSinkWants wants2;
sprangc5d62e22017-04-03 06:53:04129 wants2.max_pixel_count = 640 * 360;
perkj2d5f0912016-02-29 08:04:41130 broadcaster.AddOrUpdateSink(&sink2, wants2);
sprangc5d62e22017-04-03 06:53:04131 EXPECT_EQ(640 * 360, broadcaster.wants().max_pixel_count);
perkj2d5f0912016-02-29 08:04:41132
133 broadcaster.RemoveSink(&sink2);
sprangc5d62e22017-04-03 06:53:04134 EXPECT_EQ(1280 * 720, broadcaster.wants().max_pixel_count);
perkj2d5f0912016-02-29 08:04:41135}
136
sprang84a37592017-02-10 15:04:27137TEST(VideoBroadcasterTest, AppliesMinOfSinkWantsMaxAndTargetPixelCount) {
perkj2d5f0912016-02-29 08:04:41138 VideoBroadcaster broadcaster;
sprang84a37592017-02-10 15:04:27139 EXPECT_TRUE(!broadcaster.wants().target_pixel_count);
perkj2d5f0912016-02-29 08:04:41140
perkjd6c39542016-03-17 09:35:23141 FakeVideoRenderer sink1;
perkj2d5f0912016-02-29 08:04:41142 VideoSinkWants wants1;
Oskar Sundbom78807582017-11-16 10:09:55143 wants1.target_pixel_count = 1280 * 720;
perkj2d5f0912016-02-29 08:04:41144
145 broadcaster.AddOrUpdateSink(&sink1, wants1);
sprang84a37592017-02-10 15:04:27146 EXPECT_EQ(1280 * 720, *broadcaster.wants().target_pixel_count);
perkj2d5f0912016-02-29 08:04:41147
perkjd6c39542016-03-17 09:35:23148 FakeVideoRenderer sink2;
perkj2d5f0912016-02-29 08:04:41149 VideoSinkWants wants2;
Oskar Sundbom78807582017-11-16 10:09:55150 wants2.target_pixel_count = 640 * 360;
perkj2d5f0912016-02-29 08:04:41151 broadcaster.AddOrUpdateSink(&sink2, wants2);
sprang84a37592017-02-10 15:04:27152 EXPECT_EQ(640 * 360, *broadcaster.wants().target_pixel_count);
perkj2d5f0912016-02-29 08:04:41153
154 broadcaster.RemoveSink(&sink2);
sprang84a37592017-02-10 15:04:27155 EXPECT_EQ(1280 * 720, *broadcaster.wants().target_pixel_count);
perkj2d5f0912016-02-29 08:04:41156}
perkjd6c39542016-03-17 09:35:23157
sprangc5d62e22017-04-03 06:53:04158TEST(VideoBroadcasterTest, AppliesMinOfSinkWantsMaxFramerate) {
159 VideoBroadcaster broadcaster;
160 EXPECT_EQ(std::numeric_limits<int>::max(),
161 broadcaster.wants().max_framerate_fps);
162
163 FakeVideoRenderer sink1;
164 VideoSinkWants wants1;
165 wants1.max_framerate_fps = 30;
166
167 broadcaster.AddOrUpdateSink(&sink1, wants1);
168 EXPECT_EQ(30, broadcaster.wants().max_framerate_fps);
169
170 FakeVideoRenderer sink2;
171 VideoSinkWants wants2;
172 wants2.max_framerate_fps = 15;
173 broadcaster.AddOrUpdateSink(&sink2, wants2);
174 EXPECT_EQ(15, broadcaster.wants().max_framerate_fps);
175
176 broadcaster.RemoveSink(&sink2);
177 EXPECT_EQ(30, broadcaster.wants().max_framerate_fps);
178}
179
Rasmus Brandt5cad55b2019-12-19 08:47:11180TEST(VideoBroadcasterTest,
181 AppliesLeastCommonMultipleOfSinkWantsResolutionAlignment) {
182 VideoBroadcaster broadcaster;
183 EXPECT_EQ(broadcaster.wants().resolution_alignment, 1);
184
185 FakeVideoRenderer sink1;
186 VideoSinkWants wants1;
187 wants1.resolution_alignment = 2;
188 broadcaster.AddOrUpdateSink(&sink1, wants1);
189 EXPECT_EQ(broadcaster.wants().resolution_alignment, 2);
190
191 FakeVideoRenderer sink2;
192 VideoSinkWants wants2;
193 wants2.resolution_alignment = 3;
194 broadcaster.AddOrUpdateSink(&sink2, wants2);
195 EXPECT_EQ(broadcaster.wants().resolution_alignment, 6);
196
197 FakeVideoRenderer sink3;
198 VideoSinkWants wants3;
199 wants3.resolution_alignment = 4;
200 broadcaster.AddOrUpdateSink(&sink3, wants3);
201 EXPECT_EQ(broadcaster.wants().resolution_alignment, 12);
202
203 broadcaster.RemoveSink(&sink2);
204 EXPECT_EQ(broadcaster.wants().resolution_alignment, 4);
205}
206
perkjd6c39542016-03-17 09:35:23207TEST(VideoBroadcasterTest, SinkWantsBlackFrames) {
208 VideoBroadcaster broadcaster;
209 EXPECT_TRUE(!broadcaster.wants().black_frames);
210
211 FakeVideoRenderer sink1;
212 VideoSinkWants wants1;
213 wants1.black_frames = true;
214 broadcaster.AddOrUpdateSink(&sink1, wants1);
215
216 FakeVideoRenderer sink2;
217 VideoSinkWants wants2;
nisseefec5902016-06-09 07:31:39218 wants2.black_frames = false;
perkjd6c39542016-03-17 09:35:23219 broadcaster.AddOrUpdateSink(&sink2, wants2);
220
nisseefec5902016-06-09 07:31:39221 rtc::scoped_refptr<webrtc::I420Buffer> buffer(
nisseaf916892017-01-10 15:44:26222 webrtc::I420Buffer::Create(100, 200));
nisseefec5902016-06-09 07:31:39223 // Makes it not all black.
224 buffer->InitializeData();
225
Artem Titov1ebfb6a2019-01-03 22:49:37226 webrtc::VideoFrame frame1 = webrtc::VideoFrame::Builder()
227 .set_video_frame_buffer(buffer)
228 .set_rotation(webrtc::kVideoRotation_0)
229 .set_timestamp_us(10)
230 .build();
perkjd6c39542016-03-17 09:35:23231 broadcaster.OnFrame(frame1);
232 EXPECT_TRUE(sink1.black_frame());
nisse306d52b2016-09-06 14:52:40233 EXPECT_EQ(10, sink1.timestamp_us());
perkjd6c39542016-03-17 09:35:23234 EXPECT_FALSE(sink2.black_frame());
nisse306d52b2016-09-06 14:52:40235 EXPECT_EQ(10, sink2.timestamp_us());
perkjd6c39542016-03-17 09:35:23236
237 // Switch the sink wants.
238 wants1.black_frames = false;
239 broadcaster.AddOrUpdateSink(&sink1, wants1);
240 wants2.black_frames = true;
241 broadcaster.AddOrUpdateSink(&sink2, wants2);
242
Artem Titov1ebfb6a2019-01-03 22:49:37243 webrtc::VideoFrame frame2 = webrtc::VideoFrame::Builder()
244 .set_video_frame_buffer(buffer)
245 .set_rotation(webrtc::kVideoRotation_0)
246 .set_timestamp_us(30)
247 .build();
perkjd6c39542016-03-17 09:35:23248 broadcaster.OnFrame(frame2);
249 EXPECT_FALSE(sink1.black_frame());
nisse306d52b2016-09-06 14:52:40250 EXPECT_EQ(30, sink1.timestamp_us());
perkjd6c39542016-03-17 09:35:23251 EXPECT_TRUE(sink2.black_frame());
nisse306d52b2016-09-06 14:52:40252 EXPECT_EQ(30, sink2.timestamp_us());
perkjd6c39542016-03-17 09:35:23253}
Markus Handell6fa9e682021-10-13 20:50:53254
255TEST(VideoBroadcasterTest, ConstraintsChangedNotCalledOnSinkAddition) {
256 MockSink sink;
257 VideoBroadcaster broadcaster;
258 EXPECT_CALL(sink, OnConstraintsChanged).Times(0);
259 broadcaster.AddOrUpdateSink(&sink, VideoSinkWants());
260}
261
262TEST(VideoBroadcasterTest, ForwardsLastConstraintsOnAdd) {
263 MockSink sink;
264 VideoBroadcaster broadcaster;
265 broadcaster.ProcessConstraints(webrtc::VideoTrackSourceConstraints{2, 3});
266 broadcaster.ProcessConstraints(webrtc::VideoTrackSourceConstraints{1, 4});
267 EXPECT_CALL(
268 sink,
269 OnConstraintsChanged(AllOf(
270 Field(&webrtc::VideoTrackSourceConstraints::min_fps, Optional(1)),
271 Field(&webrtc::VideoTrackSourceConstraints::max_fps, Optional(4)))));
272 broadcaster.AddOrUpdateSink(&sink, VideoSinkWants());
273}
274
275TEST(VideoBroadcasterTest, UpdatesOnlyNewSinksWithConstraints) {
276 MockSink sink1;
277 VideoBroadcaster broadcaster;
278 broadcaster.AddOrUpdateSink(&sink1, VideoSinkWants());
279 broadcaster.ProcessConstraints(webrtc::VideoTrackSourceConstraints{1, 4});
280 Mock::VerifyAndClearExpectations(&sink1);
281 EXPECT_CALL(sink1, OnConstraintsChanged).Times(0);
282 MockSink sink2;
283 EXPECT_CALL(
284 sink2,
285 OnConstraintsChanged(AllOf(
286 Field(&webrtc::VideoTrackSourceConstraints::min_fps, Optional(1)),
287 Field(&webrtc::VideoTrackSourceConstraints::max_fps, Optional(4)))));
288 broadcaster.AddOrUpdateSink(&sink2, VideoSinkWants());
289}
290
291TEST(VideoBroadcasterTest, ForwardsConstraintsToSink) {
292 MockSink sink;
293 VideoBroadcaster broadcaster;
294 EXPECT_CALL(sink, OnConstraintsChanged).Times(0);
295 broadcaster.AddOrUpdateSink(&sink, VideoSinkWants());
296 Mock::VerifyAndClearExpectations(&sink);
297
298 EXPECT_CALL(sink, OnConstraintsChanged(AllOf(
299 Field(&webrtc::VideoTrackSourceConstraints::min_fps,
300 Eq(absl::nullopt)),
301 Field(&webrtc::VideoTrackSourceConstraints::max_fps,
302 Eq(absl::nullopt)))));
303 broadcaster.ProcessConstraints(
304 webrtc::VideoTrackSourceConstraints{absl::nullopt, absl::nullopt});
305 Mock::VerifyAndClearExpectations(&sink);
306
307 EXPECT_CALL(
308 sink,
309 OnConstraintsChanged(AllOf(
310 Field(&webrtc::VideoTrackSourceConstraints::min_fps,
311 Eq(absl::nullopt)),
312 Field(&webrtc::VideoTrackSourceConstraints::max_fps, Optional(3)))));
313 broadcaster.ProcessConstraints(
314 webrtc::VideoTrackSourceConstraints{absl::nullopt, 3});
315 Mock::VerifyAndClearExpectations(&sink);
316
317 EXPECT_CALL(
318 sink,
319 OnConstraintsChanged(AllOf(
320 Field(&webrtc::VideoTrackSourceConstraints::min_fps, Optional(2)),
321 Field(&webrtc::VideoTrackSourceConstraints::max_fps,
322 Eq(absl::nullopt)))));
323 broadcaster.ProcessConstraints(
324 webrtc::VideoTrackSourceConstraints{2, absl::nullopt});
325 Mock::VerifyAndClearExpectations(&sink);
326
327 EXPECT_CALL(
328 sink,
329 OnConstraintsChanged(AllOf(
330 Field(&webrtc::VideoTrackSourceConstraints::min_fps, Optional(2)),
331 Field(&webrtc::VideoTrackSourceConstraints::max_fps, Optional(3)))));
332 broadcaster.ProcessConstraints(webrtc::VideoTrackSourceConstraints{2, 3});
333}
Jonas Oreland0deda152022-09-23 10:08:57334
335TEST(VideoBroadcasterTest, AppliesMaxOfSinkWantsRequestedResolution) {
336 VideoBroadcaster broadcaster;
337
338 FakeVideoRenderer sink1;
339 VideoSinkWants wants1;
Jonas Oreland1d3452f2023-05-12 14:36:41340 wants1.is_active = true;
Jonas Oreland0deda152022-09-23 10:08:57341 wants1.requested_resolution = FrameSize(640, 360);
342
343 broadcaster.AddOrUpdateSink(&sink1, wants1);
344 EXPECT_EQ(FrameSize(640, 360), *broadcaster.wants().requested_resolution);
345
346 FakeVideoRenderer sink2;
347 VideoSinkWants wants2;
Jonas Oreland1d3452f2023-05-12 14:36:41348 wants2.is_active = true;
Jonas Oreland0deda152022-09-23 10:08:57349 wants2.requested_resolution = FrameSize(650, 350);
350 broadcaster.AddOrUpdateSink(&sink2, wants2);
351 EXPECT_EQ(FrameSize(650, 360), *broadcaster.wants().requested_resolution);
352
353 broadcaster.RemoveSink(&sink2);
354 EXPECT_EQ(FrameSize(640, 360), *broadcaster.wants().requested_resolution);
355}
356
357TEST(VideoBroadcasterTest, AnyActive) {
358 VideoBroadcaster broadcaster;
359
360 FakeVideoRenderer sink1;
361 VideoSinkWants wants1;
362 wants1.is_active = false;
363
364 broadcaster.AddOrUpdateSink(&sink1, wants1);
365 EXPECT_EQ(false, broadcaster.wants().is_active);
366
367 FakeVideoRenderer sink2;
368 VideoSinkWants wants2;
369 wants2.is_active = true;
370 broadcaster.AddOrUpdateSink(&sink2, wants2);
371 EXPECT_EQ(true, broadcaster.wants().is_active);
372
373 broadcaster.RemoveSink(&sink2);
374 EXPECT_EQ(false, broadcaster.wants().is_active);
375}
376
377TEST(VideoBroadcasterTest, AnyActiveWithoutRequestedResolution) {
378 VideoBroadcaster broadcaster;
379
380 FakeVideoRenderer sink1;
381 VideoSinkWants wants1;
382 wants1.is_active = true;
383 wants1.requested_resolution = FrameSize(640, 360);
384
385 broadcaster.AddOrUpdateSink(&sink1, wants1);
386 EXPECT_EQ(
387 false,
388 broadcaster.wants().aggregates->any_active_without_requested_resolution);
389
390 FakeVideoRenderer sink2;
391 VideoSinkWants wants2;
392 wants2.is_active = true;
393 broadcaster.AddOrUpdateSink(&sink2, wants2);
394 EXPECT_EQ(
395 true,
396 broadcaster.wants().aggregates->any_active_without_requested_resolution);
397
398 broadcaster.RemoveSink(&sink2);
399 EXPECT_EQ(
400 false,
401 broadcaster.wants().aggregates->any_active_without_requested_resolution);
402}
Jonas Oreland43f0f292022-10-07 13:37:17403
404// This verifies that the VideoSinkWants from a Sink that is_active = false
405// is ignored IF there is an active sink using new api (Requested_Resolution).
406// The uses resolution_alignment for verification.
407TEST(VideoBroadcasterTest, IgnoreInactiveSinkIfNewApiUsed) {
408 VideoBroadcaster broadcaster;
409
410 FakeVideoRenderer sink1;
411 VideoSinkWants wants1;
412 wants1.is_active = true;
413 wants1.requested_resolution = FrameSize(640, 360);
414 wants1.resolution_alignment = 2;
415 broadcaster.AddOrUpdateSink(&sink1, wants1);
416 EXPECT_EQ(broadcaster.wants().resolution_alignment, 2);
417
418 FakeVideoRenderer sink2;
419 VideoSinkWants wants2;
420 wants2.is_active = true;
421 wants2.resolution_alignment = 8;
422 broadcaster.AddOrUpdateSink(&sink2, wants2);
423 EXPECT_EQ(broadcaster.wants().resolution_alignment, 8);
424
425 // Now wants2 will be ignored.
426 wants2.is_active = false;
427 broadcaster.AddOrUpdateSink(&sink2, wants2);
428 EXPECT_EQ(broadcaster.wants().resolution_alignment, 2);
429
430 // But when wants1 is inactive, wants2 matters again.
431 wants1.is_active = false;
432 broadcaster.AddOrUpdateSink(&sink1, wants1);
433 EXPECT_EQ(broadcaster.wants().resolution_alignment, 8);
434
435 // inactive wants1 (new api) is always ignored.
436 broadcaster.RemoveSink(&sink2);
437 EXPECT_EQ(broadcaster.wants().resolution_alignment, 1);
438}