| /* | 
 |  *  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.requested_resolution().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, 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<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, 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<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 |