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}
This commit is contained in:

committed by
Commit Bot

parent
014197b581
commit
df95f5d43f
@ -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",
|
||||
|
@ -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<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 @@ class TestAv1Encoder {
|
||||
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 @@ 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<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 @@ class TestAv1Decoder {
|
||||
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
|
||||
|
Reference in New Issue
Block a user