diff --git a/modules/video_coding/codecs/av1/BUILD.gn b/modules/video_coding/codecs/av1/BUILD.gn index e6b689b442..301dc7b5c7 100644 --- a/modules/video_coding/codecs/av1/BUILD.gn +++ b/modules/video_coding/codecs/av1/BUILD.gn @@ -92,6 +92,7 @@ if (rtc_include_tests) { deps = [ ":libaom_av1_decoder", ":libaom_av1_encoder", + ":scalable_video_controller", "../..:video_codec_interface", "../../../../api:create_frame_generator", "../../../../api:frame_generator_api", diff --git a/modules/video_coding/codecs/av1/libaom_av1_unittest.cc b/modules/video_coding/codecs/av1/libaom_av1_unittest.cc index 4a549ea453..dfda625a35 100644 --- a/modules/video_coding/codecs/av1/libaom_av1_unittest.cc +++ b/modules/video_coding/codecs/av1/libaom_av1_unittest.cc @@ -21,6 +21,8 @@ #include "api/video_codecs/video_encoder.h" #include "modules/video_coding/codecs/av1/libaom_av1_decoder.h" #include "modules/video_coding/codecs/av1/libaom_av1_encoder.h" +#include "modules/video_coding/codecs/av1/scalable_video_controller.h" +#include "modules/video_coding/codecs/av1/scalable_video_controller_no_layering.h" #include "modules/video_coding/include/video_codec_interface.h" #include "modules/video_coding/include/video_error_codes.h" #include "test/gmock.h" @@ -29,10 +31,16 @@ namespace webrtc { namespace { +using ::testing::ContainerEq; +using ::testing::Each; using ::testing::ElementsAreArray; +using ::testing::Ge; using ::testing::IsEmpty; using ::testing::Not; using ::testing::NotNull; +using ::testing::SizeIs; +using ::testing::Truly; +using ::testing::Values; // Use small resolution for this test to make it faster. constexpr int kWidth = 320; @@ -47,19 +55,11 @@ class TestAv1Encoder { CodecSpecificInfo codec_specific_info; }; - TestAv1Encoder() : encoder_(CreateLibaomAv1Encoder()) { - RTC_CHECK(encoder_); - VideoCodec codec_settings; - codec_settings.width = kWidth; - codec_settings.height = kHeight; - codec_settings.maxFramerate = kFramerate; - VideoEncoder::Settings encoder_settings( - VideoEncoder::Capabilities(/*loss_notification=*/false), - /*number_of_cores=*/1, /*max_payload_size=*/1200); - EXPECT_EQ(encoder_->InitEncode(&codec_settings, encoder_settings), - WEBRTC_VIDEO_CODEC_OK); - EXPECT_EQ(encoder_->RegisterEncodeCompleteCallback(&callback_), - WEBRTC_VIDEO_CODEC_OK); + TestAv1Encoder() : encoder_(CreateLibaomAv1Encoder()) { InitEncoder(); } + explicit TestAv1Encoder( + std::unique_ptr svc_controller) + : encoder_(CreateLibaomAv1Encoder(std::move(svc_controller))) { + InitEncoder(); } // This class requires pointer stability and thus not copyable nor movable. TestAv1Encoder(const TestAv1Encoder&) = delete; @@ -92,16 +92,31 @@ class TestAv1Encoder { std::vector* storage_ = nullptr; }; + void InitEncoder() { + RTC_CHECK(encoder_); + VideoCodec codec_settings; + codec_settings.width = kWidth; + codec_settings.height = kHeight; + codec_settings.maxFramerate = kFramerate; + VideoEncoder::Settings encoder_settings( + VideoEncoder::Capabilities(/*loss_notification=*/false), + /*number_of_cores=*/1, /*max_payload_size=*/1200); + EXPECT_EQ(encoder_->InitEncode(&codec_settings, encoder_settings), + WEBRTC_VIDEO_CODEC_OK); + EXPECT_EQ(encoder_->RegisterEncodeCompleteCallback(&callback_), + WEBRTC_VIDEO_CODEC_OK); + } + EncoderCallback callback_; std::unique_ptr encoder_; }; class TestAv1Decoder { public: - TestAv1Decoder() { - decoder_ = CreateLibaomAv1Decoder(); + explicit TestAv1Decoder(int decoder_id) + : decoder_id_(decoder_id), decoder_(CreateLibaomAv1Decoder()) { if (decoder_ == nullptr) { - ADD_FAILURE() << "Failed to create a decoder"; + ADD_FAILURE() << "Failed to create a decoder#" << decoder_id_; return; } EXPECT_EQ(decoder_->InitDecode(/*codec_settings=*/nullptr, @@ -116,20 +131,17 @@ class TestAv1Decoder { void Decode(int64_t frame_id, const EncodedImage& image) { ASSERT_THAT(decoder_, NotNull()); - requested_ids_.push_back(frame_id); int32_t error = decoder_->Decode(image, /*missing_frames=*/false, /*render_time_ms=*/image.capture_time_ms_); if (error != WEBRTC_VIDEO_CODEC_OK) { ADD_FAILURE() << "Failed to decode frame id " << frame_id - << " with error code " << error; + << " with error code " << error << " by decoder#" + << decoder_id_; return; } decoded_ids_.push_back(frame_id); } - const std::vector& requested_frame_ids() const { - return requested_ids_; - } const std::vector& decoded_frame_ids() const { return decoded_ids_; } size_t num_output_frames() const { return callback_.num_called(); } @@ -156,51 +168,110 @@ class TestAv1Decoder { int num_called_ = 0; }; - std::vector requested_ids_; + const int decoder_id_; std::vector decoded_ids_; DecoderCallback callback_; - std::unique_ptr decoder_; + const std::unique_ptr decoder_; }; -std::vector GenerateFrames(size_t num_frames) { - std::vector frames; - frames.reserve(num_frames); - - auto input_frame_generator = test::CreateSquareFrameGenerator( - kWidth, kHeight, test::FrameGeneratorInterface::OutputType::kI420, - absl::nullopt); - uint32_t timestamp = 1000; - for (size_t i = 0; i < num_frames; ++i) { - frames.push_back( - VideoFrame::Builder() - .set_video_frame_buffer(input_frame_generator->NextFrame().buffer) - .set_timestamp_rtp(timestamp += kRtpTicksPerSecond / kFramerate) - .build()); +class VideoFrameGenerator { + public: + VideoFrame NextFrame() { + return VideoFrame::Builder() + .set_video_frame_buffer(frame_buffer_generator_->NextFrame().buffer) + .set_timestamp_rtp(timestamp_ += kRtpTicksPerSecond / kFramerate) + .build(); } - return frames; -} + + private: + uint32_t timestamp_ = 1000; + std::unique_ptr frame_buffer_generator_ = + test::CreateSquareFrameGenerator( + kWidth, + kHeight, + test::FrameGeneratorInterface::OutputType::kI420, + absl::nullopt); +}; TEST(LibaomAv1Test, EncodeDecode) { - TestAv1Decoder decoder; + TestAv1Decoder decoder(0); TestAv1Encoder encoder; + VideoFrameGenerator generator; std::vector encoded_frames; - for (const VideoFrame& frame : GenerateFrames(/*num_frames=*/4)) { - encoder.EncodeAndAppend(frame, &encoded_frames); + for (size_t i = 0; i < 4; ++i) { + encoder.EncodeAndAppend(generator.NextFrame(), &encoded_frames); } - for (size_t frame_idx = 0; frame_idx < encoded_frames.size(); ++frame_idx) { - decoder.Decode(static_cast(frame_idx), - encoded_frames[frame_idx].encoded_image); + for (size_t frame_id = 0; frame_id < encoded_frames.size(); ++frame_id) { + decoder.Decode(static_cast(frame_id), + encoded_frames[frame_id].encoded_image); } // Check encoder produced some frames for decoder to decode. ASSERT_THAT(encoded_frames, Not(IsEmpty())); // Check decoder found all of them valid. - EXPECT_THAT(decoder.decoded_frame_ids(), - ElementsAreArray(decoder.requested_frame_ids())); + EXPECT_THAT(decoder.decoded_frame_ids(), SizeIs(encoded_frames.size())); // Check each of them produced an output frame. EXPECT_EQ(decoder.num_output_frames(), decoder.decoded_frame_ids().size()); } +struct SvcTestParam { + std::function()> svc_factory; + int num_frames_to_generate; +}; + +class LibaomAv1SvcTest : public ::testing::TestWithParam {}; + +TEST_P(LibaomAv1SvcTest, EncodeAndDecodeAllDecodeTargets) { + std::unique_ptr svc_controller = + GetParam().svc_factory(); + size_t num_decode_targets = + svc_controller->DependencyStructure().num_decode_targets; + + std::vector encoded_frames; + TestAv1Encoder encoder(std::move(svc_controller)); + VideoFrameGenerator generator; + for (int temporal_unit = 0; temporal_unit < GetParam().num_frames_to_generate; + ++temporal_unit) { + encoder.EncodeAndAppend(generator.NextFrame(), &encoded_frames); + } + + ASSERT_THAT( + encoded_frames, Each(Truly([&](const TestAv1Encoder::Encoded& frame) { + return frame.codec_specific_info.generic_frame_info && + frame.codec_specific_info.generic_frame_info + ->decode_target_indications.size() == num_decode_targets; + }))); + + for (size_t dt = 0; dt < num_decode_targets; ++dt) { + TestAv1Decoder decoder(dt); + std::vector requested_ids; + for (int64_t frame_id = 0; + frame_id < static_cast(encoded_frames.size()); ++frame_id) { + const TestAv1Encoder::Encoded& frame = encoded_frames[frame_id]; + if (frame.codec_specific_info.generic_frame_info + ->decode_target_indications[dt] != + DecodeTargetIndication::kNotPresent) { + requested_ids.push_back(frame_id); + decoder.Decode(frame_id, frame.encoded_image); + } + } + + ASSERT_THAT(requested_ids, SizeIs(Ge(2u))); + // Check decoder found all of them valid. + EXPECT_THAT(decoder.decoded_frame_ids(), ContainerEq(requested_ids)) + << "Decoder#" << dt; + // Check each of them produced an output frame. + EXPECT_EQ(decoder.num_output_frames(), decoder.decoded_frame_ids().size()) + << "Decoder#" << dt; + } +} + +INSTANTIATE_TEST_SUITE_P( + Svc, + LibaomAv1SvcTest, + Values(SvcTestParam{std::make_unique, + /*num_frames_to_generate=*/4})); + } // namespace } // namespace webrtc