Add a test for OSS-Fuzz Issue 559019046 Suggested by Yuan Tong in https://github.com/AOMediaCodec/libavif/pull/1069#discussion_r4090282167 Bug: oss-fuzz:559019046 Change-Id: I1f7e280a1ec7c80a63cf3dee21c880e7d6f2fb23 (cherry picked from commit 9f9c3f7475793f1a80fc8fb811dbff84549a91df)
diff --git a/test/encode_api_test.cc b/test/encode_api_test.cc index e672822..a5eeec6 100644 --- a/test/encode_api_test.cc +++ b/test/encode_api_test.cc
@@ -772,6 +772,86 @@ ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK); } +#if !CONFIG_REALTIME_ONLY +// Test for OSS-Fuzz Issue 559019046. The bug triggers when we increase the +// number of tiles after encoding a non-intra frame and then encode a key frame. +// Based on Yuan Tong's example in +// https://github.com/AOMediaCodec/libavif/pull/1069#discussion_r4090282167: +// magick -size 128x1024 xc:gray -alpha set -channel A -evaluate set 60% +// +channel flat.png +// avifenc -s 6 -j 8 -k 2 flat.png flat.png --tilerowslog2:u 2 flat.png +// -o flat.avif +TEST(EncodeAPI, Issue559019046) { + aom_codec_iface_t *const iface = aom_codec_av1_cx(); + aom_codec_enc_cfg_t cfg; + const unsigned int usage = AOM_USAGE_GOOD_QUALITY; + ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, usage), AOM_CODEC_OK); + cfg.g_threads = 8; + cfg.g_w = 128; + cfg.g_h = 1024; + cfg.g_lag_in_frames = 0; + cfg.kf_max_dist = 2; + + aom_codec_ctx_t enc; + ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, 0), AOM_CODEC_OK); + + const int speed = 6; + ASSERT_EQ(aom_codec_control(&enc, AOME_SET_CPUUSED, speed), AOM_CODEC_OK); + + const aom_enc_frame_flags_t flags = 0; + int frame_index = 0; + + // Encode a key frame. + aom_image_t *image = CreateGrayImage(AOM_IMG_FMT_I420, cfg.g_w, cfg.g_h); + ASSERT_NE(image, nullptr); + ASSERT_EQ(aom_codec_encode(&enc, image, frame_index, 1, flags), AOM_CODEC_OK); + frame_index++; + const aom_codec_cx_pkt_t *pkt; + aom_codec_iter_t iter = nullptr; + while ((pkt = aom_codec_get_cx_data(&enc, &iter)) != nullptr) { + ASSERT_EQ(pkt->kind, AOM_CODEC_CX_FRAME_PKT); + ASSERT_EQ(pkt->data.frame.flags & AOM_FRAME_IS_KEY, AOM_FRAME_IS_KEY); + } + + // Encode a non-intra frame. + ASSERT_EQ(aom_codec_encode(&enc, image, frame_index, 1, flags), AOM_CODEC_OK); + frame_index++; + iter = nullptr; + while ((pkt = aom_codec_get_cx_data(&enc, &iter)) != nullptr) { + ASSERT_EQ(pkt->kind, AOM_CODEC_CX_FRAME_PKT); + ASSERT_EQ( + pkt->data.frame.flags & (AOM_FRAME_IS_KEY | AOM_FRAME_IS_INTRAONLY), 0); + } + + // Increase the number of tiles. + ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_TILE_ROWS, 2), AOM_CODEC_OK); + + // Encode a key frame. + ASSERT_EQ(aom_codec_encode(&enc, image, frame_index, 1, flags), AOM_CODEC_OK); + frame_index++; + iter = nullptr; + while ((pkt = aom_codec_get_cx_data(&enc, &iter)) != nullptr) { + ASSERT_EQ(pkt->kind, AOM_CODEC_CX_FRAME_PKT); + ASSERT_EQ(pkt->data.frame.flags & AOM_FRAME_IS_KEY, AOM_FRAME_IS_KEY); + } + aom_img_free(image); + + // Flush the encoder. + bool got_data; + do { + ASSERT_EQ(aom_codec_encode(&enc, nullptr, 0, 0, 0), AOM_CODEC_OK); + got_data = false; + iter = nullptr; + while ((pkt = aom_codec_get_cx_data(&enc, &iter)) != nullptr) { + ASSERT_EQ(pkt->kind, AOM_CODEC_CX_FRAME_PKT); + got_data = true; + } + } while (got_data); + + ASSERT_EQ(aom_codec_destroy(&enc), AOM_CODEC_OK); +} +#endif // !CONFIG_REALTIME_ONLY + // Emulates the WebCodecs VideoEncoder interface. class AV1Encoder { public: