/* * Copyright 2020 The WebRTC Project Authors. All rights reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "video/video_source_sink_controller.h" #include #include "api/video/video_frame.h" #include "api/video/video_source_interface.h" #include "call/adaptation/video_source_restrictions.h" #include "test/gmock.h" #include "test/gtest.h" using testing::_; namespace webrtc { namespace { using FrameSize = rtc::VideoSinkWants::FrameSize; constexpr int kIntUnconstrained = std::numeric_limits::max(); class MockVideoSinkWithVideoFrame : public rtc::VideoSinkInterface { public: ~MockVideoSinkWithVideoFrame() override {} MOCK_METHOD(void, OnFrame, (const VideoFrame& frame), (override)); MOCK_METHOD(void, OnDiscardedFrame, (), (override)); }; class MockVideoSourceWithVideoFrame : public rtc::VideoSourceInterface { public: ~MockVideoSourceWithVideoFrame() override {} MOCK_METHOD(void, AddOrUpdateSink, (rtc::VideoSinkInterface*, const rtc::VideoSinkWants&), (override)); MOCK_METHOD(void, RemoveSink, (rtc::VideoSinkInterface*), (override)); MOCK_METHOD(void, RequestRefreshFrame, (), (override)); }; } // namespace TEST(VideoSourceSinkControllerTest, UnconstrainedByDefault) { MockVideoSinkWithVideoFrame sink; MockVideoSourceWithVideoFrame source; VideoSourceSinkController controller(&sink, &source); EXPECT_EQ(controller.restrictions(), VideoSourceRestrictions()); EXPECT_FALSE(controller.pixels_per_frame_upper_limit().has_value()); EXPECT_FALSE(controller.frame_rate_upper_limit().has_value()); EXPECT_FALSE(controller.rotation_applied()); EXPECT_FALSE(controller.requested_resolution().has_value()); EXPECT_EQ(controller.resolution_alignment(), 1); EXPECT_CALL(source, AddOrUpdateSink(_, _)) .WillOnce([](rtc::VideoSinkInterface* sink, const rtc::VideoSinkWants& wants) { EXPECT_FALSE(wants.rotation_applied); EXPECT_EQ(wants.max_pixel_count, kIntUnconstrained); EXPECT_EQ(wants.target_pixel_count, absl::nullopt); EXPECT_EQ(wants.max_framerate_fps, kIntUnconstrained); EXPECT_EQ(wants.resolution_alignment, 1); EXPECT_FALSE(wants.requested_resolution.has_value()); }); controller.PushSourceSinkSettings(); } TEST(VideoSourceSinkControllerTest, VideoRestrictionsToSinkWants) { MockVideoSinkWithVideoFrame sink; MockVideoSourceWithVideoFrame source; VideoSourceSinkController controller(&sink, &source); VideoSourceRestrictions restrictions = controller.restrictions(); // max_pixels_per_frame() maps to `max_pixel_count`. restrictions.set_max_pixels_per_frame(42u); // target_pixels_per_frame() maps to `target_pixel_count`. restrictions.set_target_pixels_per_frame(200u); // max_frame_rate() maps to `max_framerate_fps`. restrictions.set_max_frame_rate(30.0); controller.SetRestrictions(restrictions); EXPECT_CALL(source, AddOrUpdateSink(_, _)) .WillOnce([](rtc::VideoSinkInterface* sink, const rtc::VideoSinkWants& wants) { EXPECT_EQ(wants.max_pixel_count, 42); EXPECT_EQ(wants.target_pixel_count, 200); EXPECT_EQ(wants.max_framerate_fps, 30); }); controller.PushSourceSinkSettings(); // pixels_per_frame_upper_limit() caps `max_pixel_count`. controller.SetPixelsPerFrameUpperLimit(24); // frame_rate_upper_limit() caps `max_framerate_fps`. controller.SetFrameRateUpperLimit(10.0); EXPECT_CALL(source, AddOrUpdateSink(_, _)) .WillOnce([](rtc::VideoSinkInterface* sink, const rtc::VideoSinkWants& wants) { EXPECT_EQ(wants.max_pixel_count, 24); EXPECT_EQ(wants.max_framerate_fps, 10); }); controller.PushSourceSinkSettings(); } TEST(VideoSourceSinkControllerTest, RotationApplied) { MockVideoSinkWithVideoFrame sink; MockVideoSourceWithVideoFrame source; VideoSourceSinkController controller(&sink, &source); controller.SetRotationApplied(true); EXPECT_TRUE(controller.rotation_applied()); EXPECT_CALL(source, AddOrUpdateSink(_, _)) .WillOnce([](rtc::VideoSinkInterface* sink, const rtc::VideoSinkWants& wants) { EXPECT_TRUE(wants.rotation_applied); }); controller.PushSourceSinkSettings(); } TEST(VideoSourceSinkControllerTest, ResolutionAlignment) { MockVideoSinkWithVideoFrame sink; MockVideoSourceWithVideoFrame source; VideoSourceSinkController controller(&sink, &source); controller.SetResolutionAlignment(13); EXPECT_EQ(controller.resolution_alignment(), 13); EXPECT_CALL(source, AddOrUpdateSink(_, _)) .WillOnce([](rtc::VideoSinkInterface* sink, const rtc::VideoSinkWants& wants) { EXPECT_EQ(wants.resolution_alignment, 13); }); controller.PushSourceSinkSettings(); } TEST(VideoSourceSinkControllerTest, PushSourceSinkSettingsWithoutSourceDoesNotCrash) { MockVideoSinkWithVideoFrame sink; VideoSourceSinkController controller(&sink, nullptr); controller.PushSourceSinkSettings(); } TEST(VideoSourceSinkControllerTest, RequestsRefreshFrameWithSource) { MockVideoSinkWithVideoFrame sink; MockVideoSourceWithVideoFrame source; VideoSourceSinkController controller(&sink, &source); EXPECT_CALL(source, RequestRefreshFrame); controller.RequestRefreshFrame(); } TEST(VideoSourceSinkControllerTest, RequestsRefreshFrameWithoutSourceDoesNotCrash) { MockVideoSinkWithVideoFrame sink; VideoSourceSinkController controller(&sink, nullptr); controller.RequestRefreshFrame(); } TEST(VideoSourceSinkControllerTest, RequestedResolutionPropagatesToWants) { MockVideoSinkWithVideoFrame sink; MockVideoSourceWithVideoFrame source; VideoSourceSinkController controller(&sink, &source); controller.SetRequestedResolution(FrameSize(640, 360)); EXPECT_TRUE(controller.requested_resolution().has_value()); EXPECT_CALL(source, AddOrUpdateSink(_, _)) .WillOnce([](rtc::VideoSinkInterface* sink, const rtc::VideoSinkWants& wants) { EXPECT_EQ(*wants.requested_resolution, FrameSize(640, 360)); }); controller.PushSourceSinkSettings(); } TEST(VideoSourceSinkControllerTest, ActivePropagatesToWants) { MockVideoSinkWithVideoFrame sink; MockVideoSourceWithVideoFrame source; VideoSourceSinkController controller(&sink, &source); controller.SetActive(true); EXPECT_TRUE(controller.active()); EXPECT_CALL(source, AddOrUpdateSink(_, _)) .WillOnce([](rtc::VideoSinkInterface* sink, const rtc::VideoSinkWants& wants) { EXPECT_TRUE(wants.is_active); }); controller.PushSourceSinkSettings(); } } // namespace webrtc