blob: bddfc0dcc77d47ffa36b1d83d5572e28e2c6e706 [file] [log] [blame]
/*
* 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, 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, 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