/* * Copyright (c) 2022 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 "modules/video_coding/codecs/test/video_codec_tester_impl.h" #include #include #include #include #include "api/units/frequency.h" #include "api/units/time_delta.h" #include "api/video/encoded_image.h" #include "api/video/i420_buffer.h" #include "api/video/video_frame.h" #include "rtc_base/fake_clock.h" #include "rtc_base/gunit.h" #include "rtc_base/task_queue_for_test.h" #include "rtc_base/time_utils.h" #include "test/gmock.h" #include "test/gtest.h" namespace webrtc { namespace test { namespace { using ::testing::_; using ::testing::Invoke; using ::testing::InvokeWithoutArgs; using ::testing::Return; using Decoder = VideoCodecTester::Decoder; using Encoder = VideoCodecTester::Encoder; using CodedVideoSource = VideoCodecTester::CodedVideoSource; using RawVideoSource = VideoCodecTester::RawVideoSource; using DecoderSettings = VideoCodecTester::DecoderSettings; using EncoderSettings = VideoCodecTester::EncoderSettings; using PacingSettings = VideoCodecTester::PacingSettings; using PacingMode = PacingSettings::PacingMode; constexpr Frequency k90kHz = Frequency::Hertz(90000); struct PacingTestParams { PacingSettings pacing_settings; Frequency framerate; int num_frames; std::vector expected_delta_ms; }; VideoFrame CreateVideoFrame(uint32_t timestamp_rtp) { rtc::scoped_refptr buffer(I420Buffer::Create(2, 2)); return VideoFrame::Builder() .set_video_frame_buffer(buffer) .set_timestamp_rtp(timestamp_rtp) .build(); } EncodedImage CreateEncodedImage(uint32_t timestamp_rtp) { EncodedImage encoded_image; encoded_image.SetRtpTimestamp(timestamp_rtp); return encoded_image; } class MockRawVideoSource : public RawVideoSource { public: MockRawVideoSource(int num_frames, Frequency framerate) : num_frames_(num_frames), frame_num_(0), framerate_(framerate) {} absl::optional PullFrame() override { if (frame_num_ >= num_frames_) { return absl::nullopt; } uint32_t timestamp_rtp = frame_num_ * k90kHz / framerate_; ++frame_num_; return CreateVideoFrame(timestamp_rtp); } MOCK_METHOD(VideoFrame, GetFrame, (uint32_t timestamp_rtp, Resolution), (override)); private: int num_frames_; int frame_num_; Frequency framerate_; }; class MockCodedVideoSource : public CodedVideoSource { public: MockCodedVideoSource(int num_frames, Frequency framerate) : num_frames_(num_frames), frame_num_(0), framerate_(framerate) {} absl::optional PullFrame() override { if (frame_num_ >= num_frames_) { return absl::nullopt; } uint32_t timestamp_rtp = frame_num_ * k90kHz / framerate_; ++frame_num_; return CreateEncodedImage(timestamp_rtp); } private: int num_frames_; int frame_num_; Frequency framerate_; }; class MockDecoder : public Decoder { public: MOCK_METHOD(void, Initialize, (), (override)); MOCK_METHOD(void, Decode, (const EncodedImage& frame, DecodeCallback callback), (override)); MOCK_METHOD(void, Flush, (), (override)); }; class MockEncoder : public Encoder { public: MOCK_METHOD(void, Initialize, (), (override)); MOCK_METHOD(void, Encode, (const VideoFrame& frame, EncodeCallback callback), (override)); MOCK_METHOD(void, Flush, (), (override)); }; } // namespace class VideoCodecTesterImplPacingTest : public ::testing::TestWithParam { public: VideoCodecTesterImplPacingTest() : test_params_(GetParam()) {} protected: PacingTestParams test_params_; }; TEST_P(VideoCodecTesterImplPacingTest, PaceEncode) { MockRawVideoSource video_source(test_params_.num_frames, test_params_.framerate); MockEncoder encoder; EncoderSettings encoder_settings; encoder_settings.pacing = test_params_.pacing_settings; VideoCodecTesterImpl tester; auto fs = tester.RunEncodeTest(&video_source, &encoder, encoder_settings)->Slice(); ASSERT_EQ(static_cast(fs.size()), test_params_.num_frames); for (size_t i = 1; i < fs.size(); ++i) { int delta_ms = (fs[i].encode_start - fs[i - 1].encode_start).ms(); EXPECT_NEAR(delta_ms, test_params_.expected_delta_ms[i - 1], 10); } } TEST_P(VideoCodecTesterImplPacingTest, PaceDecode) { MockCodedVideoSource video_source(test_params_.num_frames, test_params_.framerate); MockDecoder decoder; DecoderSettings decoder_settings; decoder_settings.pacing = test_params_.pacing_settings; VideoCodecTesterImpl tester; auto fs = tester.RunDecodeTest(&video_source, &decoder, decoder_settings)->Slice(); ASSERT_EQ(static_cast(fs.size()), test_params_.num_frames); for (size_t i = 1; i < fs.size(); ++i) { int delta_ms = (fs[i].decode_start - fs[i - 1].decode_start).ms(); EXPECT_NEAR(delta_ms, test_params_.expected_delta_ms[i - 1], 20); } } INSTANTIATE_TEST_SUITE_P( DISABLED_All, VideoCodecTesterImplPacingTest, ::testing::ValuesIn( {// No pacing. PacingTestParams({.pacing_settings = {.mode = PacingMode::kNoPacing}, .framerate = Frequency::Hertz(10), .num_frames = 3, .expected_delta_ms = {0, 0}}), // Real-time pacing. PacingTestParams({.pacing_settings = {.mode = PacingMode::kRealTime}, .framerate = Frequency::Hertz(10), .num_frames = 3, .expected_delta_ms = {100, 100}}), // Pace with specified constant rate. PacingTestParams( {.pacing_settings = {.mode = PacingMode::kConstantRate, .constant_rate = Frequency::Hertz(20)}, .framerate = Frequency::Hertz(10), .num_frames = 3, .expected_delta_ms = {50, 50}})})); } // namespace test } // namespace webrtc