Skip to content

Commit

Permalink
AOM version check for encoding
Browse files Browse the repository at this point in the history
  • Loading branch information
tongyuantongyu committed Jan 15, 2023
1 parent d7fe311 commit 5e208db
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 49 deletions.
21 changes: 20 additions & 1 deletion src/codec_aom.c
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,7 @@ static avifResult aomCodecEncodeImage(avifCodec * codec,
{
struct aom_codec_enc_cfg * cfg = &codec->internal->cfg;
avifBool quantizerUpdated = AVIF_FALSE;
const int aomVersion = aom_codec_version();

if (!codec->internal->encoderInitialized) {
// Map encoder speed to AOM usage + CpuUsed:
Expand Down Expand Up @@ -560,7 +561,6 @@ static avifResult aomCodecEncodeImage(avifCodec * codec,

// aom_codec.h says: aom_codec_version() == (major<<16 | minor<<8 | patch)
static const int aomVersion_2_0_0 = (2 << 16);
const int aomVersion = aom_codec_version();
if ((aomVersion < aomVersion_2_0_0) && (image->depth > 8)) {
// Due to a known issue with libaom v1.0.0-errata1-avif, 10bpc and
// 12bpc image encodes will call the wrong variant of
Expand Down Expand Up @@ -783,6 +783,25 @@ static avifResult aomCodecEncodeImage(avifCodec * codec,
} else {
avifBool dimensionsChanged = AVIF_FALSE;
if ((cfg->g_w != image->width) || (cfg->g_h != image->height)) {
static const int aomVersion_3_6_0 = (3 << 16) + (6 << 8);
if (aomVersion < aomVersion_3_6_0) {
// Due to a bug in libaom before v3.6.0 encoding 10bpc and 12bpc images
// with changing dimension will crash the encoder.
if (image->depth > 8) {
return AVIF_RESULT_INCOMPATIBLE_IMAGE;
}

// There exists a bug in libaom's buffer allocation logic before v3.6.0
// where it allocates buffers based on g_w and g_h of first frame instead of
// g_forced_max_frame_width and g_forced_max_frame_height, so encoding frames
// of increasing size will crash the encoder.
//
// This check is stricter than it needs to be, but we don't track the size of
// first image but only the last successful encoded one.
if ((cfg->g_w < image->width) || (cfg->g_h < image->height)) {
return AVIF_RESULT_INCOMPATIBLE_IMAGE;
}
}
cfg->g_w = image->width;
cfg->g_h = image->height;
dimensionsChanged = AVIF_TRUE;
Expand Down
112 changes: 64 additions & 48 deletions tests/gtest/avifchangedimensiontest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ namespace {

class ChangeDimensionTest
: public testing::TestWithParam<std::tuple<
/*speed=*/int, /*depth=*/int, /*maxThreads*/ int, /*tiling*/ bool,
/*size_first*/ uint32_t, /*size_second*/ uint32_t, /*speed=*/int,
/*depth=*/int, /*maxThreads*/ int, /*tiling*/ bool,
/*end_usage=*/std::string, /*tune=*/std::string, /*denoise=*/bool>> {
};

Expand All @@ -27,29 +28,31 @@ TEST_P(ChangeDimensionTest, EncodeDecode) {
GTEST_SKIP() << "Codec unavailable, skip test.";
}

const int speed = std::get<0>(GetParam());
const int depth = std::get<1>(GetParam());
const int maxThreads = std::get<2>(GetParam());
const bool tiling = std::get<3>(GetParam());
const std::string end_usage = std::get<4>(GetParam());
const std::string tune = std::get<5>(GetParam());
const bool denoise = std::get<6>(GetParam());

uint32_t size_small = 64;
uint32_t size_display = 128;
if (maxThreads > 1) {
size_small = 512;
size_display = 768;
}
const uint32_t size_first = std::get<0>(GetParam());
const uint32_t size_second = std::get<1>(GetParam());
const int speed = std::get<2>(GetParam());
const int depth = std::get<3>(GetParam());
const int maxThreads = std::get<4>(GetParam());
const bool tiling = std::get<5>(GetParam());
const std::string end_usage = std::get<6>(GetParam());
const std::string tune = std::get<7>(GetParam());
const bool denoise = std::get<8>(GetParam());

char versionBuffer[256];
avifCodecVersions(versionBuffer);
bool will_fail = (versionBuffer < std::string("v3.6.0")) &&
((size_first < size_second) || (depth > 8));

uint32_t size_display = std::max(size_first, size_second);

testutil::AvifImagePtr first = testutil::CreateImage(
int(size_small), int(size_small), depth, AVIF_PIXEL_FORMAT_YUV420,
int(size_first), int(size_first), depth, AVIF_PIXEL_FORMAT_YUV420,
AVIF_PLANES_YUV, AVIF_RANGE_FULL);
ASSERT_NE(first, nullptr);
testutil::FillImageGradient(first.get());

testutil::AvifImagePtr second = testutil::CreateImage(
int(size_display), int(size_display), depth, AVIF_PIXEL_FORMAT_YUV420,
int(size_second), int(size_second), depth, AVIF_PIXEL_FORMAT_YUV420,
AVIF_PLANES_YUV, AVIF_RANGE_FULL);
ASSERT_NE(second, nullptr);
testutil::FillImageGradient(second.get());
Expand Down Expand Up @@ -82,15 +85,18 @@ TEST_P(ChangeDimensionTest, EncodeDecode) {
}

ASSERT_EQ(avifEncoderAddImage(encoder.get(), first.get(), 1, 0),
AVIF_RESULT_OK)
<< encoder->diag.error;
AVIF_RESULT_OK);

if (will_fail) {
ASSERT_EQ(avifEncoderAddImage(encoder.get(), second.get(), 1, 0),
AVIF_RESULT_INCOMPATIBLE_IMAGE);
return;
}

ASSERT_EQ(avifEncoderAddImage(encoder.get(), second.get(), 1, 0),
AVIF_RESULT_OK)
<< encoder->diag.error;
AVIF_RESULT_OK);

ASSERT_EQ(avifEncoderFinish(encoder.get(), &encodedAvif), AVIF_RESULT_OK)
<< encoder->diag.error;
ASSERT_EQ(avifEncoderFinish(encoder.get(), &encodedAvif), AVIF_RESULT_OK);
}

// Decode
Expand All @@ -99,39 +105,49 @@ TEST_P(ChangeDimensionTest, EncodeDecode) {
ASSERT_NE(decoder, nullptr);

avifDecoderSetIOMemory(decoder.get(), encodedAvif.data, encodedAvif.size);
ASSERT_EQ(avifDecoderParse(decoder.get()), AVIF_RESULT_OK)
<< decoder->diag.error;
ASSERT_EQ(avifDecoderNextImage(decoder.get()), AVIF_RESULT_OK)
<< decoder->diag.error;
ASSERT_EQ(avifDecoderParse(decoder.get()), AVIF_RESULT_OK);
ASSERT_EQ(avifDecoderNextImage(decoder.get()), AVIF_RESULT_OK);
// libavif scales frames automatically.
ASSERT_EQ(decoder->image->width, size_display);
ASSERT_EQ(decoder->image->height, size_display);
ASSERT_EQ(avifDecoderNextImage(decoder.get()), AVIF_RESULT_OK)
<< decoder->diag.error;
ASSERT_EQ(avifDecoderNextImage(decoder.get()), AVIF_RESULT_OK);
ASSERT_EQ(decoder->image->width, size_display);
ASSERT_EQ(decoder->image->height, size_display);
}
}

INSTANTIATE_TEST_SUITE_P(
AOM, ChangeDimensionTest,
Combine(/*speed=*/Values(6, 10), // Test both GOOD_QUALITY and REALTIME
/*depth=*/Values(8, 10),
/*maxThreads*/ Values(1),
/*tiling*/ Bool(),
/*end_usage=*/Values("q", "cbr"),
/*tune=*/Values("ssim", "psnr"),
/*denoise=*/Bool()));

INSTANTIATE_TEST_SUITE_P(
AOMMultiThread, ChangeDimensionTest,
Combine(/*speed=*/Values(6, 10), // Test both GOOD_QUALITY and REALTIME
/*depth=*/Values(8, 10),
/*maxThreads*/ Values(8),
/*tiling*/ Values(true),
/*end_usage=*/Values("q"),
/*tune=*/Values("ssim"),
/*denoise=*/Values(true)));
INSTANTIATE_TEST_SUITE_P(AOMDecreasing, ChangeDimensionTest,
Combine(/*size_first*/ Values(128),
/*size_second*/ Values(64),
/*speed=*/Values(6, 10),
/*depth=*/Values(8, 10),
/*maxThreads*/ Values(1),
/*tiling*/ Bool(),
/*end_usage=*/Values("q", "cbr"),
/*tune=*/Values("ssim", "psnr"),
/*denoise=*/Bool()));

INSTANTIATE_TEST_SUITE_P(AOMIncreasing, ChangeDimensionTest,
Combine(/*size_first*/ Values(64),
/*size_second*/ Values(128),
/*speed=*/Values(6, 10),
/*depth=*/Values(8, 10),
/*maxThreads*/ Values(1),
/*tiling*/ Bool(),
/*end_usage=*/Values("q", "cbr"),
/*tune=*/Values("ssim", "psnr"),
/*denoise=*/Bool()));

INSTANTIATE_TEST_SUITE_P(AOMIncreasingMultiThread, ChangeDimensionTest,
Combine(/*size_first*/ Values(512),
/*size_second*/ Values(768),
/*speed=*/Values(6, 10),
/*depth=*/Values(8, 10),
/*maxThreads*/ Values(8),
/*tiling*/ Values(true),
/*end_usage=*/Values("q"),
/*tune=*/Values("ssim"),
/*denoise=*/Values(true)));

} // namespace
} // namespace libavif

0 comments on commit 5e208db

Please sign in to comment.