Skip to content

Commit 5e208db

Browse files
AOM version check for encoding
1 parent d7fe311 commit 5e208db

File tree

2 files changed

+84
-49
lines changed

2 files changed

+84
-49
lines changed

src/codec_aom.c

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,7 @@ static avifResult aomCodecEncodeImage(avifCodec * codec,
522522
{
523523
struct aom_codec_enc_cfg * cfg = &codec->internal->cfg;
524524
avifBool quantizerUpdated = AVIF_FALSE;
525+
const int aomVersion = aom_codec_version();
525526

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

561562
// aom_codec.h says: aom_codec_version() == (major<<16 | minor<<8 | patch)
562563
static const int aomVersion_2_0_0 = (2 << 16);
563-
const int aomVersion = aom_codec_version();
564564
if ((aomVersion < aomVersion_2_0_0) && (image->depth > 8)) {
565565
// Due to a known issue with libaom v1.0.0-errata1-avif, 10bpc and
566566
// 12bpc image encodes will call the wrong variant of
@@ -783,6 +783,25 @@ static avifResult aomCodecEncodeImage(avifCodec * codec,
783783
} else {
784784
avifBool dimensionsChanged = AVIF_FALSE;
785785
if ((cfg->g_w != image->width) || (cfg->g_h != image->height)) {
786+
static const int aomVersion_3_6_0 = (3 << 16) + (6 << 8);
787+
if (aomVersion < aomVersion_3_6_0) {
788+
// Due to a bug in libaom before v3.6.0 encoding 10bpc and 12bpc images
789+
// with changing dimension will crash the encoder.
790+
if (image->depth > 8) {
791+
return AVIF_RESULT_INCOMPATIBLE_IMAGE;
792+
}
793+
794+
// There exists a bug in libaom's buffer allocation logic before v3.6.0
795+
// where it allocates buffers based on g_w and g_h of first frame instead of
796+
// g_forced_max_frame_width and g_forced_max_frame_height, so encoding frames
797+
// of increasing size will crash the encoder.
798+
//
799+
// This check is stricter than it needs to be, but we don't track the size of
800+
// first image but only the last successful encoded one.
801+
if ((cfg->g_w < image->width) || (cfg->g_h < image->height)) {
802+
return AVIF_RESULT_INCOMPATIBLE_IMAGE;
803+
}
804+
}
786805
cfg->g_w = image->width;
787806
cfg->g_h = image->height;
788807
dimensionsChanged = AVIF_TRUE;

tests/gtest/avifchangedimensiontest.cc

Lines changed: 64 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ namespace {
1717

1818
class ChangeDimensionTest
1919
: public testing::TestWithParam<std::tuple<
20-
/*speed=*/int, /*depth=*/int, /*maxThreads*/ int, /*tiling*/ bool,
20+
/*size_first*/ uint32_t, /*size_second*/ uint32_t, /*speed=*/int,
21+
/*depth=*/int, /*maxThreads*/ int, /*tiling*/ bool,
2122
/*end_usage=*/std::string, /*tune=*/std::string, /*denoise=*/bool>> {
2223
};
2324

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

30-
const int speed = std::get<0>(GetParam());
31-
const int depth = std::get<1>(GetParam());
32-
const int maxThreads = std::get<2>(GetParam());
33-
const bool tiling = std::get<3>(GetParam());
34-
const std::string end_usage = std::get<4>(GetParam());
35-
const std::string tune = std::get<5>(GetParam());
36-
const bool denoise = std::get<6>(GetParam());
37-
38-
uint32_t size_small = 64;
39-
uint32_t size_display = 128;
40-
if (maxThreads > 1) {
41-
size_small = 512;
42-
size_display = 768;
43-
}
31+
const uint32_t size_first = std::get<0>(GetParam());
32+
const uint32_t size_second = std::get<1>(GetParam());
33+
const int speed = std::get<2>(GetParam());
34+
const int depth = std::get<3>(GetParam());
35+
const int maxThreads = std::get<4>(GetParam());
36+
const bool tiling = std::get<5>(GetParam());
37+
const std::string end_usage = std::get<6>(GetParam());
38+
const std::string tune = std::get<7>(GetParam());
39+
const bool denoise = std::get<8>(GetParam());
40+
41+
char versionBuffer[256];
42+
avifCodecVersions(versionBuffer);
43+
bool will_fail = (versionBuffer < std::string("v3.6.0")) &&
44+
((size_first < size_second) || (depth > 8));
45+
46+
uint32_t size_display = std::max(size_first, size_second);
4447

4548
testutil::AvifImagePtr first = testutil::CreateImage(
46-
int(size_small), int(size_small), depth, AVIF_PIXEL_FORMAT_YUV420,
49+
int(size_first), int(size_first), depth, AVIF_PIXEL_FORMAT_YUV420,
4750
AVIF_PLANES_YUV, AVIF_RANGE_FULL);
4851
ASSERT_NE(first, nullptr);
4952
testutil::FillImageGradient(first.get());
5053

5154
testutil::AvifImagePtr second = testutil::CreateImage(
52-
int(size_display), int(size_display), depth, AVIF_PIXEL_FORMAT_YUV420,
55+
int(size_second), int(size_second), depth, AVIF_PIXEL_FORMAT_YUV420,
5356
AVIF_PLANES_YUV, AVIF_RANGE_FULL);
5457
ASSERT_NE(second, nullptr);
5558
testutil::FillImageGradient(second.get());
@@ -82,15 +85,18 @@ TEST_P(ChangeDimensionTest, EncodeDecode) {
8285
}
8386

8487
ASSERT_EQ(avifEncoderAddImage(encoder.get(), first.get(), 1, 0),
85-
AVIF_RESULT_OK)
86-
<< encoder->diag.error;
88+
AVIF_RESULT_OK);
89+
90+
if (will_fail) {
91+
ASSERT_EQ(avifEncoderAddImage(encoder.get(), second.get(), 1, 0),
92+
AVIF_RESULT_INCOMPATIBLE_IMAGE);
93+
return;
94+
}
8795

8896
ASSERT_EQ(avifEncoderAddImage(encoder.get(), second.get(), 1, 0),
89-
AVIF_RESULT_OK)
90-
<< encoder->diag.error;
97+
AVIF_RESULT_OK);
9198

92-
ASSERT_EQ(avifEncoderFinish(encoder.get(), &encodedAvif), AVIF_RESULT_OK)
93-
<< encoder->diag.error;
99+
ASSERT_EQ(avifEncoderFinish(encoder.get(), &encodedAvif), AVIF_RESULT_OK);
94100
}
95101

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

101107
avifDecoderSetIOMemory(decoder.get(), encodedAvif.data, encodedAvif.size);
102-
ASSERT_EQ(avifDecoderParse(decoder.get()), AVIF_RESULT_OK)
103-
<< decoder->diag.error;
104-
ASSERT_EQ(avifDecoderNextImage(decoder.get()), AVIF_RESULT_OK)
105-
<< decoder->diag.error;
108+
ASSERT_EQ(avifDecoderParse(decoder.get()), AVIF_RESULT_OK);
109+
ASSERT_EQ(avifDecoderNextImage(decoder.get()), AVIF_RESULT_OK);
106110
// libavif scales frames automatically.
107111
ASSERT_EQ(decoder->image->width, size_display);
108112
ASSERT_EQ(decoder->image->height, size_display);
109-
ASSERT_EQ(avifDecoderNextImage(decoder.get()), AVIF_RESULT_OK)
110-
<< decoder->diag.error;
113+
ASSERT_EQ(avifDecoderNextImage(decoder.get()), AVIF_RESULT_OK);
111114
ASSERT_EQ(decoder->image->width, size_display);
112115
ASSERT_EQ(decoder->image->height, size_display);
113116
}
114117
}
115118

116-
INSTANTIATE_TEST_SUITE_P(
117-
AOM, ChangeDimensionTest,
118-
Combine(/*speed=*/Values(6, 10), // Test both GOOD_QUALITY and REALTIME
119-
/*depth=*/Values(8, 10),
120-
/*maxThreads*/ Values(1),
121-
/*tiling*/ Bool(),
122-
/*end_usage=*/Values("q", "cbr"),
123-
/*tune=*/Values("ssim", "psnr"),
124-
/*denoise=*/Bool()));
125-
126-
INSTANTIATE_TEST_SUITE_P(
127-
AOMMultiThread, ChangeDimensionTest,
128-
Combine(/*speed=*/Values(6, 10), // Test both GOOD_QUALITY and REALTIME
129-
/*depth=*/Values(8, 10),
130-
/*maxThreads*/ Values(8),
131-
/*tiling*/ Values(true),
132-
/*end_usage=*/Values("q"),
133-
/*tune=*/Values("ssim"),
134-
/*denoise=*/Values(true)));
119+
INSTANTIATE_TEST_SUITE_P(AOMDecreasing, ChangeDimensionTest,
120+
Combine(/*size_first*/ Values(128),
121+
/*size_second*/ Values(64),
122+
/*speed=*/Values(6, 10),
123+
/*depth=*/Values(8, 10),
124+
/*maxThreads*/ Values(1),
125+
/*tiling*/ Bool(),
126+
/*end_usage=*/Values("q", "cbr"),
127+
/*tune=*/Values("ssim", "psnr"),
128+
/*denoise=*/Bool()));
129+
130+
INSTANTIATE_TEST_SUITE_P(AOMIncreasing, ChangeDimensionTest,
131+
Combine(/*size_first*/ Values(64),
132+
/*size_second*/ Values(128),
133+
/*speed=*/Values(6, 10),
134+
/*depth=*/Values(8, 10),
135+
/*maxThreads*/ Values(1),
136+
/*tiling*/ Bool(),
137+
/*end_usage=*/Values("q", "cbr"),
138+
/*tune=*/Values("ssim", "psnr"),
139+
/*denoise=*/Bool()));
140+
141+
INSTANTIATE_TEST_SUITE_P(AOMIncreasingMultiThread, ChangeDimensionTest,
142+
Combine(/*size_first*/ Values(512),
143+
/*size_second*/ Values(768),
144+
/*speed=*/Values(6, 10),
145+
/*depth=*/Values(8, 10),
146+
/*maxThreads*/ Values(8),
147+
/*tiling*/ Values(true),
148+
/*end_usage=*/Values("q"),
149+
/*tune=*/Values("ssim"),
150+
/*denoise=*/Values(true)));
135151

136152
} // namespace
137153
} // namespace libavif

0 commit comments

Comments
 (0)