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"