Commit b155f86bc6 for aom
commit b155f86bc696c4380e7002953804866c0d2b2f33
Author: Wan-Teh Chang <wtc@google.com>
Date: Tue Feb 24 14:45:48 2026 -0800
Add codec control AOMD_SET_FRAME_SIZE_LIMIT
Add the codec control AOMD_SET_FRAME_SIZE_LIMIT for the decoder. If
nonzero, it specifies the maximum frame size (width * height). 0 (the
default) means unlimited.
Bug: 485932002
Change-Id: I7823303f9c2ec3c37988e84a04022929dee1ff40
diff --git a/aom/aomdx.h b/aom/aomdx.h
index f18cbbfab9..4c32beb4f1 100644
--- a/aom/aomdx.h
+++ b/aom/aomdx.h
@@ -466,6 +466,15 @@ enum aom_dec_control_id {
* be used.
*/
AV1D_GET_MI_INFO,
+
+ /*!\brief Codec control function to set the maximum frame size, unsigned int
+ * parameter
+ *
+ * - 0 = unlimited (default)
+ * - nonzero = frame size (width * height) must be less than or equal to this
+ * maximum
+ */
+ AOMD_SET_FRAME_SIZE_LIMIT,
};
/*!\cond */
@@ -598,6 +607,9 @@ AOM_CTRL_USE_TYPE(AOMD_GET_ORDER_HINT, unsigned int *)
// The AOM_CTRL_USE_TYPE macro can't be used with AV1D_GET_MI_INFO because
// AV1D_GET_MI_INFO takes more than one parameter.
#define AOM_CTRL_AV1D_GET_MI_INFO
+
+AOM_CTRL_USE_TYPE(AOMD_SET_FRAME_SIZE_LIMIT, unsigned int)
+#define AOM_CTRL_AOMD_SET_FRAME_SIZE_LIMIT
/*!\endcond */
/*! @} - end defgroup aom_decoder */
#ifdef __cplusplus
diff --git a/av1/av1_dx_iface.c b/av1/av1_dx_iface.c
index be4b1a7d92..f880df8aa9 100644
--- a/av1/av1_dx_iface.c
+++ b/av1/av1_dx_iface.c
@@ -62,6 +62,7 @@ struct aom_codec_alg_priv {
unsigned int is_annexb;
int operating_point;
int output_all_layers;
+ unsigned int frame_size_limit;
AVxWorker *frame_worker;
@@ -112,6 +113,8 @@ static aom_codec_err_t decoder_init(aom_codec_ctx_t *ctx) {
priv->tile_mode = 0;
priv->decode_tile_row = -1;
priv->decode_tile_col = -1;
+
+ priv->frame_size_limit = 0;
}
return AOM_CODEC_OK;
@@ -485,6 +488,8 @@ static aom_codec_err_t init_decoder(aom_codec_alg_priv_t *ctx) {
// thread or loopfilter thread.
frame_worker_data->pbi->max_threads = ctx->cfg.threads;
frame_worker_data->pbi->inv_tile_order = ctx->invert_tile_order;
+ frame_worker_data->pbi->common.decode_frame_size_limit =
+ ctx->frame_size_limit;
frame_worker_data->pbi->common.tiles.large_scale = ctx->tile_mode;
frame_worker_data->pbi->is_annexb = ctx->is_annexb;
frame_worker_data->pbi->dec_tile_row = ctx->decode_tile_row;
@@ -1584,6 +1589,12 @@ static aom_codec_err_t ctrl_set_skip_film_grain(aom_codec_alg_priv_t *ctx,
return AOM_CODEC_OK;
}
+static aom_codec_err_t ctrl_set_frame_size_limit(aom_codec_alg_priv_t *ctx,
+ va_list args) {
+ ctx->frame_size_limit = va_arg(args, unsigned int);
+ return AOM_CODEC_OK;
+}
+
static aom_codec_err_t ctrl_get_accounting(aom_codec_alg_priv_t *ctx,
va_list args) {
#if !CONFIG_ACCOUNTING
@@ -1691,6 +1702,7 @@ static aom_codec_ctrl_fn_map_t decoder_ctrl_maps[] = {
{ AV1D_SET_ROW_MT, ctrl_set_row_mt },
{ AV1D_SET_EXT_REF_PTR, ctrl_set_ext_ref_ptr },
{ AV1D_SET_SKIP_FILM_GRAIN, ctrl_set_skip_film_grain },
+ { AOMD_SET_FRAME_SIZE_LIMIT, ctrl_set_frame_size_limit },
// Getters
{ AOMD_GET_FRAME_CORRUPTED, ctrl_get_frame_corrupted },
diff --git a/av1/common/av1_common_int.h b/av1/common/av1_common_int.h
index 715fe2ced7..b3edab0d66 100644
--- a/av1/common/av1_common_int.h
+++ b/av1/common/av1_common_int.h
@@ -821,6 +821,13 @@ typedef struct AV1Common {
*/
uint8_t superres_scale_denominator;
+ /*!
+ * If nonzero, the maximum frame size (width * height). 0 means unlimited.
+ * Note: Only used by the decoder. Defined in the AV1_COMMON struct for
+ * convenience.
+ */
+ unsigned int decode_frame_size_limit;
+
/*!
* buffer_removal_times[op_num] specifies the frame removal time in units of
* DecCT clock ticks counted from the removal time of the last random access
diff --git a/av1/decoder/decodeframe.c b/av1/decoder/decodeframe.c
index 69e1e13e38..32974c215d 100644
--- a/av1/decoder/decodeframe.c
+++ b/av1/decoder/decodeframe.c
@@ -1957,6 +1957,12 @@ static inline void resize_context_buffers(AV1_COMMON *cm, int width,
"Dimensions of %dx%d beyond allowed size of %dx%d.",
width, height, DECODE_WIDTH_LIMIT, DECODE_HEIGHT_LIMIT);
#endif
+ if (cm->decode_frame_size_limit &&
+ (uint64_t)width * height > cm->decode_frame_size_limit) {
+ aom_internal_error(cm->error, AOM_CODEC_CORRUPT_FRAME,
+ "Dimensions of %dx%d beyond allowed size of %u.", width,
+ height, cm->decode_frame_size_limit);
+ }
if (cm->width != width || cm->height != height) {
const int new_mi_rows = CEIL_POWER_OF_TWO(height, MI_SIZE_LOG2);
const int new_mi_cols = CEIL_POWER_OF_TWO(width, MI_SIZE_LOG2);
diff --git a/test/decode_frame_size_limit_test.cc b/test/decode_frame_size_limit_test.cc
new file mode 100644
index 0000000000..11d49215c2
--- /dev/null
+++ b/test/decode_frame_size_limit_test.cc
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2026, Alliance for Open Media. All rights reserved.
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#include <ostream>
+
+#include "gtest/gtest.h"
+#include "test/codec_factory.h"
+#include "test/decode_test_driver.h"
+#include "test/ivf_video_source.h"
+#include "test/util.h"
+#include "test/video_source.h"
+
+namespace {
+
+struct DecodeParam {
+ const char *filename;
+ unsigned int width;
+ unsigned int height;
+};
+
+std::ostream &operator<<(std::ostream &os, const DecodeParam &dp) {
+ return os << "file: " << dp.filename;
+}
+
+class DecodeFrameSizeLimitTest
+ : public ::libaom_test::DecoderTest,
+ public ::libaom_test::CodecTestWithParam<DecodeParam> {
+ protected:
+ DecodeFrameSizeLimitTest()
+ : DecoderTest(GET_PARAM(0)), width_(GET_PARAM(1).width),
+ height_(GET_PARAM(1).height) {}
+
+ ~DecodeFrameSizeLimitTest() override = default;
+
+ void PreDecodeFrameHook(const libaom_test::CompressedVideoSource &video,
+ libaom_test::Decoder *decoder) override {
+ if (video.frame_number() == 0)
+ decoder->Control(AOMD_SET_FRAME_SIZE_LIMIT, frame_size_limit_);
+ }
+
+ void DecompressedFrameHook(const aom_image_t &img,
+ const unsigned int /*frame_number*/) override {
+ EXPECT_EQ(img.d_w, width_);
+ EXPECT_EQ(img.d_h, height_);
+ }
+
+ bool HandleDecodeResult(const aom_codec_err_t res_dec,
+ const libaom_test::CompressedVideoSource & /*video*/,
+ libaom_test::Decoder * /*decoder*/) override {
+ bool expect_failure =
+ frame_size_limit_ && width_ * height_ > frame_size_limit_;
+ if (expect_failure) {
+ EXPECT_EQ(res_dec, AOM_CODEC_CORRUPT_FRAME);
+ } else {
+ EXPECT_EQ(res_dec, AOM_CODEC_OK);
+ }
+
+ return !HasFailure();
+ }
+
+ void RunTest() {
+ const DecodeParam input = GET_PARAM(1);
+ aom_codec_dec_cfg_t cfg = { 1, 0, 0, !FORCE_HIGHBITDEPTH_DECODING };
+ libaom_test::IVFVideoSource decode_video(input.filename);
+ decode_video.Init();
+
+ ASSERT_NO_FATAL_FAILURE(RunLoop(&decode_video, cfg));
+ }
+
+ protected:
+ unsigned int frame_size_limit_ = 0;
+
+ private:
+ unsigned int width_;
+ unsigned int height_;
+};
+
+TEST_P(DecodeFrameSizeLimitTest, Unlimited) { RunTest(); }
+
+TEST_P(DecodeFrameSizeLimitTest, LimitedBig) {
+ frame_size_limit_ = 226 * 210;
+ RunTest();
+}
+
+TEST_P(DecodeFrameSizeLimitTest, LimitedSmall) {
+ frame_size_limit_ = 226 * 210 - 1;
+ RunTest();
+}
+
+const DecodeParam kAV1DecodeFrameSizeLimitTests[] = {
+ // { filename, width, height }
+ { "av1-1-b8-01-size-16x16.ivf", 16, 16 },
+ { "av1-1-b8-01-size-226x210.ivf", 226, 210 },
+};
+
+AV1_INSTANTIATE_TEST_SUITE(DecodeFrameSizeLimitTest,
+ ::testing::ValuesIn(kAV1DecodeFrameSizeLimitTests));
+
+} // namespace
diff --git a/test/test.cmake b/test/test.cmake
index 9e5e91588f..8877b55364 100644
--- a/test/test.cmake
+++ b/test/test.cmake
@@ -51,6 +51,7 @@ list(APPEND AOM_UNIT_TEST_COMMON_SOURCES
add_to_libaom_test_srcs(AOM_UNIT_TEST_COMMON_SOURCES)
list(APPEND AOM_UNIT_TEST_DECODER_SOURCES "${AOM_ROOT}/test/decode_api_test.cc"
+ "${AOM_ROOT}/test/decode_frame_size_limit_test.cc"
"${AOM_ROOT}/test/decode_scalability_test.cc"
"${AOM_ROOT}/test/external_frame_buffer_test.cc"
"${AOM_ROOT}/test/invalid_file_test.cc"