video_broadcaster_unittest.cc (14709B)
1 /* 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 11 #include "media/base/video_broadcaster.h" 12 13 #include <limits> 14 #include <optional> 15 16 #include "api/scoped_refptr.h" 17 #include "api/video/i420_buffer.h" 18 #include "api/video/video_frame.h" 19 #include "api/video/video_rotation.h" 20 #include "api/video/video_sink_interface.h" 21 #include "api/video/video_source_interface.h" 22 #include "api/video_track_source_constraints.h" 23 #include "media/base/fake_video_renderer.h" 24 #include "test/gmock.h" 25 #include "test/gtest.h" 26 27 using ::webrtc::FakeVideoRenderer; 28 using ::webrtc::VideoBroadcaster; 29 using ::webrtc::VideoSinkWants; 30 using FrameSize = webrtc::VideoSinkWants::FrameSize; 31 32 using ::testing::AllOf; 33 using ::testing::Eq; 34 using ::testing::Field; 35 using ::testing::Mock; 36 using ::testing::Optional; 37 38 class MockSink : public webrtc::VideoSinkInterface<webrtc::VideoFrame> { 39 public: 40 void OnFrame(const webrtc::VideoFrame&) override {} 41 42 MOCK_METHOD(void, 43 OnConstraintsChanged, 44 (const webrtc::VideoTrackSourceConstraints& constraints), 45 (override)); 46 }; 47 48 TEST(VideoBroadcasterTest, frame_wanted) { 49 VideoBroadcaster broadcaster; 50 EXPECT_FALSE(broadcaster.frame_wanted()); 51 52 FakeVideoRenderer sink; 53 broadcaster.AddOrUpdateSink(&sink, webrtc::VideoSinkWants()); 54 EXPECT_TRUE(broadcaster.frame_wanted()); 55 56 broadcaster.RemoveSink(&sink); 57 EXPECT_FALSE(broadcaster.frame_wanted()); 58 } 59 60 TEST(VideoBroadcasterTest, OnFrame) { 61 VideoBroadcaster broadcaster; 62 63 FakeVideoRenderer sink1; 64 FakeVideoRenderer sink2; 65 broadcaster.AddOrUpdateSink(&sink1, webrtc::VideoSinkWants()); 66 broadcaster.AddOrUpdateSink(&sink2, webrtc::VideoSinkWants()); 67 static int kWidth = 100; 68 static int kHeight = 50; 69 70 webrtc::scoped_refptr<webrtc::I420Buffer> buffer( 71 webrtc::I420Buffer::Create(kWidth, kHeight)); 72 // Initialize, to avoid warnings on use of initialized values. 73 webrtc::I420Buffer::SetBlack(buffer.get()); 74 75 webrtc::VideoFrame frame = webrtc::VideoFrame::Builder() 76 .set_video_frame_buffer(buffer) 77 .set_rotation(webrtc::kVideoRotation_0) 78 .set_timestamp_us(0) 79 .build(); 80 81 broadcaster.OnFrame(frame); 82 EXPECT_EQ(1, sink1.num_rendered_frames()); 83 EXPECT_EQ(1, sink2.num_rendered_frames()); 84 85 broadcaster.RemoveSink(&sink1); 86 broadcaster.OnFrame(frame); 87 EXPECT_EQ(1, sink1.num_rendered_frames()); 88 EXPECT_EQ(2, sink2.num_rendered_frames()); 89 90 broadcaster.AddOrUpdateSink(&sink1, webrtc::VideoSinkWants()); 91 broadcaster.OnFrame(frame); 92 EXPECT_EQ(2, sink1.num_rendered_frames()); 93 EXPECT_EQ(3, sink2.num_rendered_frames()); 94 } 95 96 TEST(VideoBroadcasterTest, AppliesRotationIfAnySinkWantsRotationApplied) { 97 VideoBroadcaster broadcaster; 98 EXPECT_FALSE(broadcaster.wants().rotation_applied); 99 100 FakeVideoRenderer sink1; 101 VideoSinkWants wants1; 102 wants1.rotation_applied = false; 103 104 broadcaster.AddOrUpdateSink(&sink1, wants1); 105 EXPECT_FALSE(broadcaster.wants().rotation_applied); 106 107 FakeVideoRenderer sink2; 108 VideoSinkWants wants2; 109 wants2.rotation_applied = true; 110 111 broadcaster.AddOrUpdateSink(&sink2, wants2); 112 EXPECT_TRUE(broadcaster.wants().rotation_applied); 113 114 broadcaster.RemoveSink(&sink2); 115 EXPECT_FALSE(broadcaster.wants().rotation_applied); 116 } 117 118 TEST(VideoBroadcasterTest, AppliesMinOfSinkWantsMaxPixelCount) { 119 VideoBroadcaster broadcaster; 120 EXPECT_EQ(std::numeric_limits<int>::max(), 121 broadcaster.wants().max_pixel_count); 122 123 FakeVideoRenderer sink1; 124 VideoSinkWants wants1; 125 wants1.max_pixel_count = 1280 * 720; 126 127 broadcaster.AddOrUpdateSink(&sink1, wants1); 128 EXPECT_EQ(1280 * 720, broadcaster.wants().max_pixel_count); 129 130 FakeVideoRenderer sink2; 131 VideoSinkWants wants2; 132 wants2.max_pixel_count = 640 * 360; 133 broadcaster.AddOrUpdateSink(&sink2, wants2); 134 EXPECT_EQ(640 * 360, broadcaster.wants().max_pixel_count); 135 136 broadcaster.RemoveSink(&sink2); 137 EXPECT_EQ(1280 * 720, broadcaster.wants().max_pixel_count); 138 } 139 140 TEST(VideoBroadcasterTest, AppliesMinOfSinkWantsMaxAndTargetPixelCount) { 141 VideoBroadcaster broadcaster; 142 EXPECT_TRUE(!broadcaster.wants().target_pixel_count); 143 144 FakeVideoRenderer sink1; 145 VideoSinkWants wants1; 146 wants1.target_pixel_count = 1280 * 720; 147 148 broadcaster.AddOrUpdateSink(&sink1, wants1); 149 EXPECT_EQ(1280 * 720, *broadcaster.wants().target_pixel_count); 150 151 FakeVideoRenderer sink2; 152 VideoSinkWants wants2; 153 wants2.target_pixel_count = 640 * 360; 154 broadcaster.AddOrUpdateSink(&sink2, wants2); 155 EXPECT_EQ(640 * 360, *broadcaster.wants().target_pixel_count); 156 157 broadcaster.RemoveSink(&sink2); 158 EXPECT_EQ(1280 * 720, *broadcaster.wants().target_pixel_count); 159 } 160 161 TEST(VideoBroadcasterTest, AppliesMinOfSinkWantsMaxFramerate) { 162 VideoBroadcaster broadcaster; 163 EXPECT_EQ(std::numeric_limits<int>::max(), 164 broadcaster.wants().max_framerate_fps); 165 166 FakeVideoRenderer sink1; 167 VideoSinkWants wants1; 168 wants1.max_framerate_fps = 30; 169 170 broadcaster.AddOrUpdateSink(&sink1, wants1); 171 EXPECT_EQ(30, broadcaster.wants().max_framerate_fps); 172 173 FakeVideoRenderer sink2; 174 VideoSinkWants wants2; 175 wants2.max_framerate_fps = 15; 176 broadcaster.AddOrUpdateSink(&sink2, wants2); 177 EXPECT_EQ(15, broadcaster.wants().max_framerate_fps); 178 179 broadcaster.RemoveSink(&sink2); 180 EXPECT_EQ(30, broadcaster.wants().max_framerate_fps); 181 } 182 183 TEST(VideoBroadcasterTest, 184 AppliesLeastCommonMultipleOfSinkWantsResolutionAlignment) { 185 VideoBroadcaster broadcaster; 186 EXPECT_EQ(broadcaster.wants().resolution_alignment, 1); 187 188 FakeVideoRenderer sink1; 189 VideoSinkWants wants1; 190 wants1.resolution_alignment = 2; 191 broadcaster.AddOrUpdateSink(&sink1, wants1); 192 EXPECT_EQ(broadcaster.wants().resolution_alignment, 2); 193 194 FakeVideoRenderer sink2; 195 VideoSinkWants wants2; 196 wants2.resolution_alignment = 3; 197 broadcaster.AddOrUpdateSink(&sink2, wants2); 198 EXPECT_EQ(broadcaster.wants().resolution_alignment, 6); 199 200 FakeVideoRenderer sink3; 201 VideoSinkWants wants3; 202 wants3.resolution_alignment = 4; 203 broadcaster.AddOrUpdateSink(&sink3, wants3); 204 EXPECT_EQ(broadcaster.wants().resolution_alignment, 12); 205 206 broadcaster.RemoveSink(&sink2); 207 EXPECT_EQ(broadcaster.wants().resolution_alignment, 4); 208 } 209 210 TEST(VideoBroadcasterTest, SinkWantsBlackFrames) { 211 VideoBroadcaster broadcaster; 212 EXPECT_TRUE(!broadcaster.wants().black_frames); 213 214 FakeVideoRenderer sink1; 215 VideoSinkWants wants1; 216 wants1.black_frames = true; 217 broadcaster.AddOrUpdateSink(&sink1, wants1); 218 219 FakeVideoRenderer sink2; 220 VideoSinkWants wants2; 221 wants2.black_frames = false; 222 broadcaster.AddOrUpdateSink(&sink2, wants2); 223 224 webrtc::scoped_refptr<webrtc::I420Buffer> buffer( 225 webrtc::I420Buffer::Create(100, 200)); 226 // Makes it not all black. 227 buffer->InitializeData(); 228 229 webrtc::VideoFrame frame1 = webrtc::VideoFrame::Builder() 230 .set_video_frame_buffer(buffer) 231 .set_rotation(webrtc::kVideoRotation_0) 232 .set_timestamp_us(10) 233 .build(); 234 broadcaster.OnFrame(frame1); 235 EXPECT_TRUE(sink1.black_frame()); 236 EXPECT_EQ(10, sink1.timestamp_us()); 237 EXPECT_FALSE(sink2.black_frame()); 238 EXPECT_EQ(10, sink2.timestamp_us()); 239 240 // Switch the sink wants. 241 wants1.black_frames = false; 242 broadcaster.AddOrUpdateSink(&sink1, wants1); 243 wants2.black_frames = true; 244 broadcaster.AddOrUpdateSink(&sink2, wants2); 245 246 webrtc::VideoFrame frame2 = webrtc::VideoFrame::Builder() 247 .set_video_frame_buffer(buffer) 248 .set_rotation(webrtc::kVideoRotation_0) 249 .set_timestamp_us(30) 250 .build(); 251 broadcaster.OnFrame(frame2); 252 EXPECT_FALSE(sink1.black_frame()); 253 EXPECT_EQ(30, sink1.timestamp_us()); 254 EXPECT_TRUE(sink2.black_frame()); 255 EXPECT_EQ(30, sink2.timestamp_us()); 256 } 257 258 TEST(VideoBroadcasterTest, ConstraintsChangedNotCalledOnSinkAddition) { 259 MockSink sink; 260 VideoBroadcaster broadcaster; 261 EXPECT_CALL(sink, OnConstraintsChanged).Times(0); 262 broadcaster.AddOrUpdateSink(&sink, VideoSinkWants()); 263 } 264 265 TEST(VideoBroadcasterTest, ForwardsLastConstraintsOnAdd) { 266 MockSink sink; 267 VideoBroadcaster broadcaster; 268 broadcaster.ProcessConstraints({.min_fps = 2, .max_fps = 3}); 269 broadcaster.ProcessConstraints({.min_fps = 1, .max_fps = 4}); 270 EXPECT_CALL( 271 sink, 272 OnConstraintsChanged(AllOf( 273 Field(&webrtc::VideoTrackSourceConstraints::min_fps, Optional(1)), 274 Field(&webrtc::VideoTrackSourceConstraints::max_fps, Optional(4))))); 275 broadcaster.AddOrUpdateSink(&sink, VideoSinkWants()); 276 } 277 278 TEST(VideoBroadcasterTest, UpdatesOnlyNewSinksWithConstraints) { 279 MockSink sink1; 280 VideoBroadcaster broadcaster; 281 broadcaster.AddOrUpdateSink(&sink1, VideoSinkWants()); 282 broadcaster.ProcessConstraints({.min_fps = 1, .max_fps = 4}); 283 Mock::VerifyAndClearExpectations(&sink1); 284 EXPECT_CALL(sink1, OnConstraintsChanged).Times(0); 285 MockSink sink2; 286 EXPECT_CALL( 287 sink2, 288 OnConstraintsChanged(AllOf( 289 Field(&webrtc::VideoTrackSourceConstraints::min_fps, Optional(1)), 290 Field(&webrtc::VideoTrackSourceConstraints::max_fps, Optional(4))))); 291 broadcaster.AddOrUpdateSink(&sink2, VideoSinkWants()); 292 } 293 294 TEST(VideoBroadcasterTest, ForwardsConstraintsToSink) { 295 MockSink sink; 296 VideoBroadcaster broadcaster; 297 EXPECT_CALL(sink, OnConstraintsChanged).Times(0); 298 broadcaster.AddOrUpdateSink(&sink, VideoSinkWants()); 299 Mock::VerifyAndClearExpectations(&sink); 300 301 EXPECT_CALL(sink, OnConstraintsChanged(AllOf( 302 Field(&webrtc::VideoTrackSourceConstraints::min_fps, 303 Eq(std::nullopt)), 304 Field(&webrtc::VideoTrackSourceConstraints::max_fps, 305 Eq(std::nullopt))))); 306 broadcaster.ProcessConstraints( 307 {.min_fps = std::nullopt, .max_fps = std::nullopt}); 308 Mock::VerifyAndClearExpectations(&sink); 309 310 EXPECT_CALL( 311 sink, 312 OnConstraintsChanged(AllOf( 313 Field(&webrtc::VideoTrackSourceConstraints::min_fps, 314 Eq(std::nullopt)), 315 Field(&webrtc::VideoTrackSourceConstraints::max_fps, Optional(3))))); 316 broadcaster.ProcessConstraints({.min_fps = std::nullopt, .max_fps = 3}); 317 Mock::VerifyAndClearExpectations(&sink); 318 319 EXPECT_CALL( 320 sink, 321 OnConstraintsChanged(AllOf( 322 Field(&webrtc::VideoTrackSourceConstraints::min_fps, Optional(2)), 323 Field(&webrtc::VideoTrackSourceConstraints::max_fps, 324 Eq(std::nullopt))))); 325 broadcaster.ProcessConstraints({.min_fps = 2, .max_fps = std::nullopt}); 326 Mock::VerifyAndClearExpectations(&sink); 327 328 EXPECT_CALL( 329 sink, 330 OnConstraintsChanged(AllOf( 331 Field(&webrtc::VideoTrackSourceConstraints::min_fps, Optional(2)), 332 Field(&webrtc::VideoTrackSourceConstraints::max_fps, Optional(3))))); 333 broadcaster.ProcessConstraints({.min_fps = 2, .max_fps = 3}); 334 } 335 336 TEST(VideoBroadcasterTest, AppliesMaxOfSinkWantsScaleResolutionDownTo) { 337 VideoBroadcaster broadcaster; 338 339 FakeVideoRenderer sink1; 340 VideoSinkWants wants1; 341 wants1.is_active = true; 342 wants1.requested_resolution = FrameSize(640, 360); 343 344 broadcaster.AddOrUpdateSink(&sink1, wants1); 345 EXPECT_EQ(FrameSize(640, 360), *broadcaster.wants().requested_resolution); 346 347 FakeVideoRenderer sink2; 348 VideoSinkWants wants2; 349 wants2.is_active = true; 350 wants2.requested_resolution = FrameSize(650, 350); 351 broadcaster.AddOrUpdateSink(&sink2, wants2); 352 EXPECT_EQ(FrameSize(650, 360), *broadcaster.wants().requested_resolution); 353 354 broadcaster.RemoveSink(&sink2); 355 EXPECT_EQ(FrameSize(640, 360), *broadcaster.wants().requested_resolution); 356 } 357 358 TEST(VideoBroadcasterTest, AnyActive) { 359 VideoBroadcaster broadcaster; 360 361 FakeVideoRenderer sink1; 362 VideoSinkWants wants1; 363 wants1.is_active = false; 364 365 broadcaster.AddOrUpdateSink(&sink1, wants1); 366 EXPECT_EQ(false, broadcaster.wants().is_active); 367 368 FakeVideoRenderer sink2; 369 VideoSinkWants wants2; 370 wants2.is_active = true; 371 broadcaster.AddOrUpdateSink(&sink2, wants2); 372 EXPECT_EQ(true, broadcaster.wants().is_active); 373 374 broadcaster.RemoveSink(&sink2); 375 EXPECT_EQ(false, broadcaster.wants().is_active); 376 } 377 378 TEST(VideoBroadcasterTest, AnyActiveWithoutScaleResolutionDownTo) { 379 VideoBroadcaster broadcaster; 380 381 FakeVideoRenderer sink1; 382 VideoSinkWants wants1; 383 wants1.is_active = true; 384 wants1.requested_resolution = FrameSize(640, 360); 385 386 broadcaster.AddOrUpdateSink(&sink1, wants1); 387 EXPECT_EQ( 388 false, 389 broadcaster.wants().aggregates->any_active_without_requested_resolution); 390 391 FakeVideoRenderer sink2; 392 VideoSinkWants wants2; 393 wants2.is_active = true; 394 broadcaster.AddOrUpdateSink(&sink2, wants2); 395 EXPECT_EQ( 396 true, 397 broadcaster.wants().aggregates->any_active_without_requested_resolution); 398 399 broadcaster.RemoveSink(&sink2); 400 EXPECT_EQ( 401 false, 402 broadcaster.wants().aggregates->any_active_without_requested_resolution); 403 } 404 405 // This verifies that the VideoSinkWants from a Sink that is_active = false 406 // is ignored IF there is an active sink using requested_resolution (controlled 407 // via new API scale_resolution_down_to). The uses resolution_alignment for 408 // verification. 409 TEST(VideoBroadcasterTest, IgnoreInactiveSinkIfNewApiUsed) { 410 VideoBroadcaster broadcaster; 411 412 FakeVideoRenderer sink1; 413 VideoSinkWants wants1; 414 wants1.is_active = true; 415 wants1.requested_resolution = FrameSize(640, 360); 416 wants1.resolution_alignment = 2; 417 broadcaster.AddOrUpdateSink(&sink1, wants1); 418 EXPECT_EQ(broadcaster.wants().resolution_alignment, 2); 419 420 FakeVideoRenderer sink2; 421 VideoSinkWants wants2; 422 wants2.is_active = true; 423 wants2.resolution_alignment = 8; 424 broadcaster.AddOrUpdateSink(&sink2, wants2); 425 EXPECT_EQ(broadcaster.wants().resolution_alignment, 8); 426 427 // Now wants2 will be ignored. 428 wants2.is_active = false; 429 broadcaster.AddOrUpdateSink(&sink2, wants2); 430 EXPECT_EQ(broadcaster.wants().resolution_alignment, 2); 431 432 // But when wants1 is inactive, wants2 matters again. 433 wants1.is_active = false; 434 broadcaster.AddOrUpdateSink(&sink1, wants1); 435 EXPECT_EQ(broadcaster.wants().resolution_alignment, 8); 436 437 // inactive wants1 (new api) is always ignored. 438 broadcaster.RemoveSink(&sink2); 439 EXPECT_EQ(broadcaster.wants().resolution_alignment, 1); 440 }