| /* |
| * 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 <limits> |
| |
| #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<int>::max(); |
| |
| class MockVideoSinkWithVideoFrame : public rtc::VideoSinkInterface<VideoFrame> { |
| public: |
| ~MockVideoSinkWithVideoFrame() override {} |
| |
| MOCK_METHOD(void, OnFrame, (const VideoFrame& frame), (override)); |
| MOCK_METHOD(void, OnDiscardedFrame, (), (override)); |
| }; |
| |
| class MockVideoSourceWithVideoFrame |
| : public rtc::VideoSourceInterface<VideoFrame> { |
| public: |
| ~MockVideoSourceWithVideoFrame() override {} |
| |
| MOCK_METHOD(void, |
| AddOrUpdateSink, |
| (rtc::VideoSinkInterface<VideoFrame>*, |
| const rtc::VideoSinkWants&), |
| (override)); |
| MOCK_METHOD(void, |
| RemoveSink, |
| (rtc::VideoSinkInterface<VideoFrame>*), |
| (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.scale_resolution_down_to().has_value()); |
| EXPECT_EQ(controller.resolution_alignment(), 1); |
| |
| EXPECT_CALL(source, AddOrUpdateSink(_, _)) |
| .WillOnce([](rtc::VideoSinkInterface<VideoFrame>* sink, |
| const rtc::VideoSinkWants& wants) { |
| EXPECT_FALSE(wants.rotation_applied); |
| EXPECT_EQ(wants.max_pixel_count, kIntUnconstrained); |
| EXPECT_EQ(wants.target_pixel_count, std::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<VideoFrame>* 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<VideoFrame>* 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<VideoFrame>* 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<VideoFrame>* 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, ScaleResolutionDownToPropagatesToWants) { |
| MockVideoSinkWithVideoFrame sink; |
| MockVideoSourceWithVideoFrame source; |
| VideoSourceSinkController controller(&sink, &source); |
| controller.SetScaleResolutionDownTo(FrameSize(640, 360)); |
| EXPECT_TRUE(controller.scale_resolution_down_to().has_value()); |
| |
| EXPECT_CALL(source, AddOrUpdateSink(_, _)) |
| .WillOnce([](rtc::VideoSinkInterface<VideoFrame>* 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<VideoFrame>* sink, |
| const rtc::VideoSinkWants& wants) { |
| EXPECT_TRUE(wants.is_active); |
| }); |
| controller.PushSourceSinkSettings(); |
| } |
| |
| } // namespace webrtc |