Add parametrized unit tests for av1 to check scalability structures

Bug: webrtc:11404
Change-Id: If92a4b0a0a78a12ff43ec3a27b189cdc7218c9c7
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/175601
Reviewed-by: Philip Eliasson <philipel@webrtc.org>
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#31365}
diff --git a/modules/video_coding/codecs/av1/BUILD.gn b/modules/video_coding/codecs/av1/BUILD.gn
index e6b689b..301dc7b 100644
--- a/modules/video_coding/codecs/av1/BUILD.gn
+++ b/modules/video_coding/codecs/av1/BUILD.gn
@@ -92,6 +92,7 @@
       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 4a549ea..dfda625 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 @@
     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<ScalableVideoController> 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 @@
     std::vector<Encoded>* 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<VideoEncoder> 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 @@
 
   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<int64_t>& requested_frame_ids() const {
-    return requested_ids_;
-  }
   const std::vector<int64_t>& decoded_frame_ids() const { return decoded_ids_; }
   size_t num_output_frames() const { return callback_.num_called(); }
 
@@ -156,51 +168,110 @@
     int num_called_ = 0;
   };
 
-  std::vector<int64_t> requested_ids_;
+  const int decoder_id_;
   std::vector<int64_t> decoded_ids_;
   DecoderCallback callback_;
-  std::unique_ptr<VideoDecoder> decoder_;
+  const std::unique_ptr<VideoDecoder> decoder_;
 };
 
-std::vector<VideoFrame> GenerateFrames(size_t num_frames) {
-  std::vector<VideoFrame> 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<test::FrameGeneratorInterface> 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<TestAv1Encoder::Encoded> 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<int64_t>(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<int64_t>(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<std::unique_ptr<ScalableVideoController>()> svc_factory;
+  int num_frames_to_generate;
+};
+
+class LibaomAv1SvcTest : public ::testing::TestWithParam<SvcTestParam> {};
+
+TEST_P(LibaomAv1SvcTest, EncodeAndDecodeAllDecodeTargets) {
+  std::unique_ptr<ScalableVideoController> svc_controller =
+      GetParam().svc_factory();
+  size_t num_decode_targets =
+      svc_controller->DependencyStructure().num_decode_targets;
+
+  std::vector<TestAv1Encoder::Encoded> 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<int64_t> requested_ids;
+    for (int64_t frame_id = 0;
+         frame_id < static_cast<int64_t>(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<ScalableVideoControllerNoLayering>,
+                        /*num_frames_to_generate=*/4}));
+
 }  // namespace
 }  // namespace webrtc