Commit 88c46fce27 for ffmpeg

commit 88c46fce27e311c73772d9b83db4047dd83b1294
Author: Ling, Edison <Edison.Ling@amd.com>
Date:   Fri Aug 7 14:53:46 2026 -0400

    avcodec/amfenc: Replace hardcoded lookahead maximum with runtime query

    Replace the hardcoded preanalysis lookahead maximum with the maximum
    reported by AMF at runtime. Query GetPropertyInfo() only when both the
    AMF SDK headers and loaded runtime are version 1.5.3 or later.

    Older SDK headers or runtimes retain the established maximum of 41.
    This avoids ABI-dependent vtable and structure-layout workarounds while
    allowing supported newer AMF installations to report their own limit.

    Clamp requested lookahead and async-depth values to the reported
    maximum, or to the legacy fallback when the query is unavailable.

diff --git a/libavcodec/amfenc.c b/libavcodec/amfenc.c
index 20bc22d98c..99b48a279b 100644
--- a/libavcodec/amfenc.c
+++ b/libavcodec/amfenc.c
@@ -36,6 +36,8 @@
 #include "amfenc.h"
 #include "encode.h"

+#include <AMF/components/PreAnalysis.h>
+
 #define AMF_AV_FRAME_REF    L"av_frame_ref"
 #define PTS_PROP            L"PtsProp"

@@ -166,6 +168,77 @@ int av_cold ff_amf_encode_close(AVCodecContext *avctx)
     return 0;
 }

+int ff_amf_get_lookahead_depth_max(AVCodecContext *avctx, AMFComponent *encoder, amf_uint64 runtime_version, amf_int64 *out_max)
+{
+    if (!encoder || !out_max)
+        return AVERROR(EINVAL);
+
+    *out_max = MAX_LOOKAHEAD_DEPTH;
+#if AMF_VERSION_ATLEAST(1, 5, 3)
+    const AMFPropertyInfo *prop_info = NULL;
+    AMF_RESULT res;
+
+    if (runtime_version >= AMF_MAKE_FULL_VERSION(1, 5, 3, 0)) {
+        res = encoder->pVtbl->GetPropertyInfo(encoder, AMF_PA_LOOKAHEAD_BUFFER_DEPTH, &prop_info);
+        if (res == AMF_OK && prop_info && prop_info->type == AMF_VARIANT_INT64) {
+            *out_max = prop_info->maxValue.int64Value;
+        } else {
+            av_log(avctx, AV_LOG_DEBUG, "Failed to query maximum lookahead depth, using %d\n", MAX_LOOKAHEAD_DEPTH);
+        }
+    }
+#endif
+    return 0;
+}
+
+void ff_amf_clamp_lookahead_depth(AVCodecContext *avctx, AMFEncoderContext *ctx)
+{
+    AVHWDeviceContext *device_ctx = (AVHWDeviceContext *)ctx->device_ctx_ref->data;
+    AVAMFDeviceContext *amf_ctx = device_ctx->hwctx;
+    amf_int64 hw_max;
+    AMF_RESULT res;
+
+    if (ff_amf_get_lookahead_depth_max(avctx, ctx->encoder, amf_ctx->version, &hw_max) == 0 &&
+        ctx->pa_lookahead_buffer_depth > hw_max) {
+        av_log(avctx, AV_LOG_WARNING,
+            "pa_lookahead_buffer_depth %d exceeds supported maximum %lld, clamping\n",
+            ctx->pa_lookahead_buffer_depth, (long long)hw_max);
+        ctx->pa_lookahead_buffer_depth = (int)hw_max;
+    }
+
+    AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_LOOKAHEAD_BUFFER_DEPTH, ctx->pa_lookahead_buffer_depth);
+    if (res != AMF_OK)
+        av_log(avctx, AV_LOG_WARNING, "Setting lookahead buffer depth failed with error %d\n", res);
+}
+
+void ff_amf_fit_async_depth(AVCodecContext *avctx, AMFEncoderContext *ctx)
+{
+    AVHWDeviceContext *device_ctx = (AVHWDeviceContext *)ctx->device_ctx_ref->data;
+    AVAMFDeviceContext *amf_ctx = device_ctx->hwctx;
+    // Size async_depth to the lookahead window in two independent steps:
+    //   - bump: widen async_depth to fit the lookahead depth read back from the
+    //     encoder (the applied value; only present when preanalysis is enabled).
+    //   - cap: limit async_depth to the reported maximum + 1 when it can be queried.
+    amf_int64 hw_max;
+    AMFVariantStruct var = { 0 };
+    AMF_RESULT res;
+
+    res = ctx->encoder->pVtbl->GetProperty(ctx->encoder, AMF_PA_LOOKAHEAD_BUFFER_DEPTH, &var);
+    if (res == AMF_OK && var.int64Value >= ctx->hwsurfaces_in_queue_max) {
+        av_log(avctx, AV_LOG_WARNING,
+            "async_depth (%d) too small for lookahead (%lld), increasing to (%lld)\n",
+            ctx->hwsurfaces_in_queue_max, (long long)var.int64Value, (long long)(var.int64Value + 1));
+        ctx->hwsurfaces_in_queue_max = (int)(var.int64Value + 1);
+    }
+
+    if (ff_amf_get_lookahead_depth_max(avctx, ctx->encoder, amf_ctx->version, &hw_max) == 0 &&
+        ctx->hwsurfaces_in_queue_max > hw_max + 1) {
+        av_log(avctx, AV_LOG_WARNING,
+            "async_depth %d exceeds supported range, clamping to %lld\n",
+            ctx->hwsurfaces_in_queue_max, (long long)(hw_max + 1));
+        ctx->hwsurfaces_in_queue_max = (int)(hw_max + 1);
+    }
+}
+
 static int amf_copy_surface(AVCodecContext *avctx, const AVFrame *frame,
     AMFSurface* surface)
 {
@@ -326,15 +399,6 @@ int ff_amf_encode_init(AVCodecContext *avctx)
         AMF_RETURN_IF_FALSE(ctx, ret == 0, ret, "Failed to create  hardware device context (AMF) : %s\n", av_err2str(ret));
     }

-    if (ctx->pa_lookahead_buffer_depth >= ctx->hwsurfaces_in_queue_max) {
-        av_log(avctx, AV_LOG_WARNING,
-               "async_depth (%d) too small for lookahead (%d), increasing to (%d)\n",
-                ctx->hwsurfaces_in_queue_max,
-                ctx->pa_lookahead_buffer_depth,
-                ctx->pa_lookahead_buffer_depth + 1);
-        ctx->hwsurfaces_in_queue_max = ctx->pa_lookahead_buffer_depth + 1;
-    }
-
     if ((ret = amf_init_encoder(avctx)) == 0) {
         return 0;
     }
diff --git a/libavcodec/amfenc.h b/libavcodec/amfenc.h
index 6a380208d1..02f3634bb5 100644
--- a/libavcodec/amfenc.h
+++ b/libavcodec/amfenc.h
@@ -31,7 +31,10 @@
 #include "avcodec.h"
 #include "hwconfig.h"

-#define  MAX_LOOKAHEAD_DEPTH 41
+#define AMF_VERSION_ATLEAST(major, minor, release) \
+    (AMF_VERSION_MAJOR > (major) || (AMF_VERSION_MAJOR == (major) && AMF_VERSION_MINOR > (minor)) || (AMF_VERSION_MAJOR == (major) && AMF_VERSION_MINOR == (minor) && AMF_VERSION_RELEASE >= (release)))
+
+#define MAX_LOOKAHEAD_DEPTH 41

 /**
 * AMF encoder context
@@ -159,6 +162,28 @@ int ff_amf_encode_close(AVCodecContext *avctx);
 */
 int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt);

+/**
+* Query the maximum PA lookahead buffer depth (AMF_PA_LOOKAHEAD_BUFFER_DEPTH).
+* Falls back to MAX_LOOKAHEAD_DEPTH for older headers/runtimes or query failure.
+* @return 0 and sets *out_max on success; a negative AVERROR on failure.
+*/
+int ff_amf_get_lookahead_depth_max(AVCodecContext *avctx, AMFComponent *encoder, amf_uint64 runtime_version, amf_int64 *out_max);
+
+/**
+* Clamp pa_lookahead_buffer_depth to the AMF-reported maximum and assign it to
+* the encoder. Call from within the preanalysis configuration block, before
+* encoder Init().
+*/
+void ff_amf_clamp_lookahead_depth(AVCodecContext *avctx, AMFEncoderContext *ctx);
+
+/**
+* Size async_depth (hwsurfaces_in_queue_max) around the lookahead window: widen
+* it to hold the lookahead depth read back from the encoder, then cap it at the
+* AMF-reported lookahead maximum + 1. Call before encoder Init(), after all
+* lookahead/B-frame properties have been set.
+*/
+void ff_amf_fit_async_depth(AVCodecContext *avctx, AMFEncoderContext *ctx);
+
 /**
 * Supported formats
 */
diff --git a/libavcodec/amfenc_av1.c b/libavcodec/amfenc_av1.c
index a572d439a7..c70c4d7b3b 100644
--- a/libavcodec/amfenc_av1.c
+++ b/libavcodec/amfenc_av1.c
@@ -101,7 +101,7 @@ static const AVOption options[] = {
     { "gop",                    "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE_GOP_ALIGNED       }, 0, 0, VE, .unit = "hdrmode" },
     { "frame",                  "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE_KEY_FRAME_ALIGNED }, 0, 0, VE, .unit = "hdrmode" },

-    { "async_depth",            "Set maximum encoding parallelism. Higher values increase output latency.", OFFSET(hwsurfaces_in_queue_max), AV_OPT_TYPE_INT, {.i64 = 16 }, 1, MAX_LOOKAHEAD_DEPTH + 1, VE },
+    { "async_depth",            "Set maximum encoding parallelism. Higher values increase output latency.", OFFSET(hwsurfaces_in_queue_max), AV_OPT_TYPE_INT, {.i64 = 16 }, 1, INT_MAX, VE },

     { "preencode",              "Enable preencode",     OFFSET(preencode),      AV_OPT_TYPE_BOOL, {.i64 = -1  }, -1, 1, VE},
     { "enforce_hrd",            "Enforce HRD",          OFFSET(enforce_hrd),    AV_OPT_TYPE_BOOL, {.i64 = -1  }, -1, 1, VE},
@@ -170,7 +170,7 @@ static const AVOption options[] = {

     { "pa_frame_sad_enable",                    "Enable Frame SAD algorithm",                                   OFFSET(pa_frame_sad),                           AV_OPT_TYPE_BOOL,   {.i64 = -1 }, -1, 1, VE },
     { "pa_ltr_enable",                          "Enable long term reference frame management",                  OFFSET(pa_ltr),                                 AV_OPT_TYPE_BOOL,   {.i64 = -1 }, -1, 1, VE },
-    { "pa_lookahead_buffer_depth",              "Sets the PA lookahead buffer size",                            OFFSET(pa_lookahead_buffer_depth),              AV_OPT_TYPE_INT,    {.i64 = -1 }, -1, MAX_LOOKAHEAD_DEPTH, VE },
+    { "pa_lookahead_buffer_depth",              "Sets the PA lookahead buffer size",                            OFFSET(pa_lookahead_buffer_depth),              AV_OPT_TYPE_INT,    {.i64 = -1 }, -1, INT_MAX, VE },

     { "pa_paq_mode",                            "Sets the perceptual adaptive quantization mode",               OFFSET(pa_paq_mode),                            AV_OPT_TYPE_INT,    {.i64 = -1 }, -1, AMF_PA_PAQ_MODE_CAQ, VE , .unit = "paq_mode" },
     { "none",                                   "no perceptual adaptive quantization",  0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_PAQ_MODE_NONE }, 0, 0, VE, .unit = "paq_mode" },
@@ -505,9 +505,8 @@ static av_cold int amf_encode_init_av1(AVCodecContext* avctx)
         if (ctx->pa_ltr != -1) {
             AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_LTR_ENABLE, ((ctx->pa_ltr == 0) ? false : true));
         }
-        if (ctx->pa_lookahead_buffer_depth != -1) {
-            AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_LOOKAHEAD_BUFFER_DEPTH, ctx->pa_lookahead_buffer_depth);
-        }
+        if (ctx->pa_lookahead_buffer_depth != -1)
+            ff_amf_clamp_lookahead_depth(avctx, ctx);
         if (ctx->pa_high_motion_quality_boost_mode != -1) {
             AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE, ctx->pa_high_motion_quality_boost_mode);
         }
@@ -574,6 +573,8 @@ static av_cold int amf_encode_init_av1(AVCodecContext* avctx)
     res = ctx->encoder->pVtbl->GetProperty(ctx->encoder, AMF_VIDEO_ENCODER_AV1_QUERY_TIMEOUT, &var);
     ctx->query_timeout_supported = res == AMF_OK && var.int64Value;

+    ff_amf_fit_async_depth(avctx, ctx);
+
     // init encoder
     res = ctx->encoder->pVtbl->Init(ctx->encoder, ctx->format, avctx->width, avctx->height);
     AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_BUG, "encoder->Init() failed with error %d\n", res);
diff --git a/libavcodec/amfenc_h264.c b/libavcodec/amfenc_h264.c
index 214fac6909..fc1c91d3a4 100644
--- a/libavcodec/amfenc_h264.c
+++ b/libavcodec/amfenc_h264.c
@@ -114,7 +114,7 @@ static const AVOption options[] = {
     { "header_spacing", "Header Insertion Spacing",             OFFSET(header_spacing),     AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1000, VE },

     /// Maximum queued frames
-    { "async_depth",    "Set maximum encoding parallelism. Higher values increase output latency.", OFFSET(hwsurfaces_in_queue_max), AV_OPT_TYPE_INT, {.i64 = 16 }, 1, MAX_LOOKAHEAD_DEPTH + 1, VE },
+    { "async_depth",    "Set maximum encoding parallelism. Higher values increase output latency.", OFFSET(hwsurfaces_in_queue_max), AV_OPT_TYPE_INT, {.i64 = 16 }, 1, INT_MAX, VE },

     /// B-Frames
     // BPicturesPattern=bf
@@ -176,7 +176,7 @@ static const AVOption options[] = {

     { "pa_frame_sad_enable",                    "Enable Frame SAD algorithm",                                   OFFSET(pa_frame_sad),                           AV_OPT_TYPE_BOOL,   {.i64 = -1 }, -1, 1, VE },
     { "pa_ltr_enable",                          "Enable long term reference frame management",                  OFFSET(pa_ltr),                                 AV_OPT_TYPE_BOOL,   {.i64 = -1 }, -1, 1, VE },
-    { "pa_lookahead_buffer_depth",              "Sets the PA lookahead buffer size",                            OFFSET(pa_lookahead_buffer_depth),              AV_OPT_TYPE_INT,    {.i64 = -1 }, -1, MAX_LOOKAHEAD_DEPTH, VE },
+    { "pa_lookahead_buffer_depth",              "Sets the PA lookahead buffer size",                            OFFSET(pa_lookahead_buffer_depth),              AV_OPT_TYPE_INT,    {.i64 = -1 }, -1, INT_MAX, VE },

     { "pa_paq_mode",                            "Sets the perceptual adaptive quantization mode",               OFFSET(pa_paq_mode),                            AV_OPT_TYPE_INT,    {.i64 = -1 }, -1, AMF_PA_PAQ_MODE_CAQ, VE , .unit = "paq_mode" },
     { "none",                                   "no perceptual adaptive quantization",  0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_PAQ_MODE_NONE }, 0, 0, VE, .unit = "paq_mode" },
@@ -466,9 +466,8 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx)
         if (ctx->pa_ltr != -1) {
             AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_LTR_ENABLE, ((ctx->pa_ltr == 0) ? false : true));
         }
-        if (ctx->pa_lookahead_buffer_depth != -1) {
-            AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_LOOKAHEAD_BUFFER_DEPTH, ctx->pa_lookahead_buffer_depth);
-        }
+        if (ctx->pa_lookahead_buffer_depth != -1)
+            ff_amf_clamp_lookahead_depth(avctx, ctx);
         if (ctx->pa_high_motion_quality_boost_mode != -1) {
             AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE, ctx->pa_high_motion_quality_boost_mode);
         }
@@ -552,6 +551,8 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx)
     res = ctx->encoder->pVtbl->GetProperty(ctx->encoder, AMF_VIDEO_ENCODER_QUERY_TIMEOUT, &var);
     ctx->query_timeout_supported = res == AMF_OK && var.int64Value;

+    ff_amf_fit_async_depth(avctx, ctx);
+
     // Initialize Encoder
     res = ctx->encoder->pVtbl->Init(ctx->encoder, ctx->format, avctx->width, avctx->height);
     AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_BUG, "encoder->Init() failed with error %d\n", res);
diff --git a/libavcodec/amfenc_hevc.c b/libavcodec/amfenc_hevc.c
index 4e280fcf15..7b91a6a5cf 100644
--- a/libavcodec/amfenc_hevc.c
+++ b/libavcodec/amfenc_hevc.c
@@ -91,7 +91,7 @@ static const AVOption options[] = {
     { "gop",            "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_GOP_ALIGNED }, 0, 0, VE, .unit = "hdrmode" },
     { "idr",            "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_IDR_ALIGNED }, 0, 0, VE, .unit = "hdrmode" },

-    { "async_depth",    "Set maximum encoding parallelism. Higher values increase output latency.",             OFFSET(hwsurfaces_in_queue_max), AV_OPT_TYPE_INT, {.i64 = 16 }, 1, MAX_LOOKAHEAD_DEPTH + 1, VE },
+    { "async_depth",    "Set maximum encoding parallelism. Higher values increase output latency.",             OFFSET(hwsurfaces_in_queue_max), AV_OPT_TYPE_INT, {.i64 = 16 }, 1, INT_MAX, VE },

     { "high_motion_quality_boost_enable",   "Enable High motion quality boost mode",  OFFSET(hw_high_motion_quality_boost), AV_OPT_TYPE_BOOL,   {.i64 = -1 }, -1, 1, VE },
     { "gops_per_idr",   "GOPs per IDR 0-no IDR will be inserted",   OFFSET(gops_per_idr),  AV_OPT_TYPE_INT,  { .i64 = 1  },  0, INT_MAX, VE },
@@ -147,7 +147,7 @@ static const AVOption options[] = {

     { "pa_frame_sad_enable",                    "Enable Frame SAD algorithm",                                   OFFSET(pa_frame_sad),                           AV_OPT_TYPE_BOOL,   {.i64 = -1 }, -1, 1, VE },
     { "pa_ltr_enable",                          "Enable long term reference frame management",                  OFFSET(pa_ltr),                                 AV_OPT_TYPE_BOOL,   {.i64 = -1 }, -1, 1, VE },
-    { "pa_lookahead_buffer_depth",              "Sets the PA lookahead buffer size",                            OFFSET(pa_lookahead_buffer_depth),              AV_OPT_TYPE_INT,    {.i64 = -1 }, -1, MAX_LOOKAHEAD_DEPTH, VE },
+    { "pa_lookahead_buffer_depth",              "Sets the PA lookahead buffer size",                            OFFSET(pa_lookahead_buffer_depth),              AV_OPT_TYPE_INT,    {.i64 = -1 }, -1, INT_MAX, VE },

     { "pa_paq_mode",                            "Sets the perceptual adaptive quantization mode",               OFFSET(pa_paq_mode),                            AV_OPT_TYPE_INT,    {.i64 = -1 }, -1, AMF_PA_PAQ_MODE_CAQ, VE , .unit = "paq_mode" },
     { "none",                                   "no perceptual adaptive quantization",  0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_PAQ_MODE_NONE }, 0, 0, VE, .unit = "paq_mode" },
@@ -471,9 +471,8 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx)
         if (ctx->pa_ltr != -1) {
             AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_LTR_ENABLE, ((ctx->pa_ltr == 0) ? false : true));
         }
-        if (ctx->pa_lookahead_buffer_depth != -1) {
-            AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_LOOKAHEAD_BUFFER_DEPTH, ctx->pa_lookahead_buffer_depth);
-        }
+        if (ctx->pa_lookahead_buffer_depth != -1)
+            ff_amf_clamp_lookahead_depth(avctx, ctx);
         if (ctx->pa_high_motion_quality_boost_mode != -1) {
             AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE, ctx->pa_high_motion_quality_boost_mode);
         }
@@ -484,6 +483,8 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx)
     res = ctx->encoder->pVtbl->GetProperty(ctx->encoder, AMF_VIDEO_ENCODER_HEVC_QUERY_TIMEOUT, &var);
     ctx->query_timeout_supported = res == AMF_OK && var.int64Value;

+    ff_amf_fit_async_depth(avctx, ctx);
+
     // init encoder
     res = ctx->encoder->pVtbl->Init(ctx->encoder, ctx->format, avctx->width, avctx->height);
     AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_BUG, "encoder->Init() failed with error %d\n", res);