PR #25286 opened by Forgejo_Fairy
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25286
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25286.patch

Fixes #24838.

A failed picture `issue()` leaves reference state added by 
`hw_base_encode_pick_next()` while the picture remains unissued. A later send 
or flush can select it again and assert when adding `prev`. Store the issue 
error and return it on subsequent receive attempts instead of revisiting that 
state.

Add a hardware-free FATE test covering synchronous and asynchronous paths. 
Before the fix, its second call aborts; with the fix, both calls return the 
original error and `issue()` runs once.

Verified on x86-64 with `fate-hw-base-issue-error` and `fate-libavcodec`. The 
attached Intel VAAPI reproducer could not start here because the available 
render node provided no VA display.


>From 4c20c7408fab6cbcf89c07a00a10d3a9965eab4b Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Wed, 21 Oct 2026 05:03:41 +0000
Subject: [PATCH 1/2] avcodec/hw_base_encode: retain issue errors across calls

An issue failure can leave the selected picture and its dependency graph 
partially updated. Return the same error on subsequent receive attempts instead 
of selecting the picture again and adding its references twice.

Fixes: #24838

Assisted-by: Fairy
---
 libavcodec/hw_base_encode.c | 5 +++++
 libavcodec/hw_base_encode.h | 2 ++
 2 files changed, 7 insertions(+)

diff --git a/libavcodec/hw_base_encode.c b/libavcodec/hw_base_encode.c
index 307e76ed79d..4e92ea44459 100644
--- a/libavcodec/hw_base_encode.c
+++ b/libavcodec/hw_base_encode.c
@@ -569,6 +569,9 @@ int ff_hw_base_encode_receive_packet(FFHWBaseEncodeContext 
*ctx,
     av_assert0(ctx->op && ctx->op->init && ctx->op->issue &&
                ctx->op->output && ctx->op->free);
 
+    if (ctx->issue_error < 0)
+        return ctx->issue_error;
+
 start:
     /** if no B frame before repeat P frame, sent repeat P frame out. */
     if (ctx->tail_pkt->size) {
@@ -609,6 +612,7 @@ start:
                 err = ctx->op->issue(avctx, pic);
                 if (err < 0) {
                     av_log(avctx, AV_LOG_ERROR, "Encode failed: %s.\n", 
av_err2str(err));
+                    ctx->issue_error = err;
                     return err;
                 }
                 pic->encode_issued = 1;
@@ -636,6 +640,7 @@ start:
         err = ctx->op->issue(avctx, pic);
         if (err < 0) {
             av_log(avctx, AV_LOG_ERROR, "Encode failed: %s.\n", 
av_err2str(err));
+            ctx->issue_error = err;
             return err;
         }
 
diff --git a/libavcodec/hw_base_encode.h b/libavcodec/hw_base_encode.h
index e9ab96fd6d4..0dcbcc48d79 100644
--- a/libavcodec/hw_base_encode.h
+++ b/libavcodec/hw_base_encode.h
@@ -197,6 +197,8 @@ typedef struct FFHWBaseEncodeContext {
     int idr_counter;
     int gop_counter;
     int end_of_stream;
+    // A failed issue leaves the picture dependency state unusable.
+    int issue_error;
     int p_to_gpb;
 
     // The number of L0/L1 references supported by the driver.
-- 
2.54.0


>From 8c457c2d426b340a82774044ede8c94c33f74d76 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Wed, 21 Oct 2026 05:05:48 +0000
Subject: [PATCH 2/2] avcodec/tests: cover repeated calls after a hardware
 issue error

Exercise synchronous and asynchronous hw_base_encode paths with an operation 
that fails on issue. FATE checks that subsequent calls return the same error 
without reissuing the picture.

Assisted-by: Fairy
---
 libavcodec/Makefile                    |   1 +
 libavcodec/tests/hw_base_issue_error.c | 111 +++++++++++++++++++++++++
 tests/fate/libavcodec.mak              |   4 +
 tests/ref/fate/hw-base-issue-error     |   2 +
 4 files changed, 118 insertions(+)
 create mode 100644 libavcodec/tests/hw_base_issue_error.c
 create mode 100644 tests/ref/fate/hw-base-issue-error

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 3d0be563200..488e8adf0cc 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1397,6 +1397,7 @@ TESTPROGS-$(CONFIG_AV1_VAAPI_ENCODER)     += av1_levels
 TESTPROGS-$(CONFIG_CABAC)                 += cabac
 TESTPROGS-$(CONFIG_CELP_MATH)             += celp_math
 TESTPROGS-$(CONFIG_GOLOMB)                += golomb
+TESTPROGS-$(CONFIG_VAAPI_ENCODE)          += hw_base_issue_error
 TESTPROGS-$(CONFIG_IDCTDSP)               += dct
 TESTPROGS-$(CONFIG_DXV_ENCODER)           += hashtable
 TESTPROGS-$(CONFIG_MJPEG_ENCODER)         += mjpegenc_huffman
diff --git a/libavcodec/tests/hw_base_issue_error.c 
b/libavcodec/tests/hw_base_issue_error.c
new file mode 100644
index 00000000000..8cc714c2dea
--- /dev/null
+++ b/libavcodec/tests/hw_base_issue_error.c
@@ -0,0 +1,111 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+
+#include "libavutil/error.h"
+#include "libavutil/log.h"
+#include "libavutil/mem.h"
+
+#include "libavcodec/hw_base_encode.h"
+#include "libavcodec/internal.h"
+
+static int issue_count;
+
+static int pic_init(AVCodecContext *avctx, FFHWBaseEncodePicture *pic)
+{
+    return 0;
+}
+
+static int pic_issue(AVCodecContext *avctx, FFHWBaseEncodePicture *pic)
+{
+    issue_count++;
+    return AVERROR_EXTERNAL;
+}
+
+static int pic_output(AVCodecContext *avctx, FFHWBaseEncodePicture *pic,
+                      AVPacket *pkt)
+{
+    return 0;
+}
+
+static int pic_free(AVCodecContext *avctx, FFHWBaseEncodePicture *pic)
+{
+    return 0;
+}
+
+static int test_issue_error(int async)
+{
+    static const FFHWEncodePictureOperation op = {
+        .priv_size = 1,
+        .init      = pic_init,
+        .issue     = pic_issue,
+        .output    = pic_output,
+        .free      = pic_free,
+    };
+    AVCodecInternal internal = { .draining = 1 };
+    AVCodecContext avctx = { .internal = &internal, .codec_id = 
AV_CODEC_ID_HEVC };
+    FFHWBaseEncodeContext ctx = { .op = &op, .gop_size = 100,
+                                  .gop_counter = 1, .ref_l0 = 1,
+                                  .input_order = 2, .nb_next_prev = 1,
+                                  .async_encode = async };
+    FFHWBaseEncodePicture *prev, *pic;
+    AVPacket *pkt;
+    int first, second;
+
+    ctx.frame = av_frame_alloc();
+    ctx.tail_pkt = av_packet_alloc();
+    prev = av_mallocz(sizeof(*prev));
+    pic = av_mallocz(sizeof(*pic));
+    pkt = av_packet_alloc();
+    if (async)
+        ctx.encode_fifo = av_fifo_alloc2(1, sizeof(pic), 0);
+    if (!ctx.frame || !ctx.tail_pkt || !prev || !pic || !pkt ||
+        (async && !ctx.encode_fifo))
+        return 1;
+
+    // A completed IDR precedes the P-picture whose issue will fail.
+    prev->type            = FF_HW_PICTURE_TYPE_IDR;
+    prev->encode_issued   = 1;
+    prev->encode_complete = 1;
+    prev->ref_count[0]    = 1;
+    prev->next            = pic;
+    pic->type            = FF_HW_PICTURE_TYPE_P;
+    pic->display_order   = 1;
+    ctx.pic_start        = prev;
+    ctx.pic_end          = pic;
+    ctx.next_prev[0]     = prev;
+
+    issue_count = 0;
+    first  = ff_hw_base_encode_receive_packet(&ctx, &avctx, pkt);
+    second = ff_hw_base_encode_receive_packet(&ctx, &avctx, pkt);
+    printf("%s first=%d second=%d issue_calls=%d\n",
+           async ? "async" : "sync", first, second, issue_count);
+
+    ff_hw_base_encode_close(&ctx);
+    av_packet_free(&pkt);
+    return 0;
+}
+
+int main(void)
+{
+    av_log_set_level(AV_LOG_QUIET);
+    if (test_issue_error(0) || test_issue_error(1))
+        return 1;
+    return 0;
+}
diff --git a/tests/fate/libavcodec.mak b/tests/fate/libavcodec.mak
index 3cde003212c..883f6fc60eb 100644
--- a/tests/fate/libavcodec.mak
+++ b/tests/fate/libavcodec.mak
@@ -12,6 +12,10 @@ fate-apv-entropy: libavcodec/tests/apv$(EXESUF)
 fate-apv-entropy: CMD = run libavcodec/tests/apv$(EXESUF)
 fate-apv-entropy: REF = /dev/null
 
+FATE_LIBAVCODEC-$(CONFIG_VAAPI_ENCODE) += fate-hw-base-issue-error
+fate-hw-base-issue-error: libavcodec/tests/hw_base_issue_error$(EXESUF)
+fate-hw-base-issue-error: CMD = run 
libavcodec/tests/hw_base_issue_error$(EXESUF)
+
 FATE_LIBAVCODEC-yes += fate-avpacket
 fate-avpacket: libavcodec/tests/avpacket$(EXESUF)
 fate-avpacket: CMD = run libavcodec/tests/avpacket$(EXESUF)
diff --git a/tests/ref/fate/hw-base-issue-error 
b/tests/ref/fate/hw-base-issue-error
new file mode 100644
index 00000000000..8c2f679837f
--- /dev/null
+++ b/tests/ref/fate/hw-base-issue-error
@@ -0,0 +1,2 @@
+sync first=-542398533 second=-542398533 issue_calls=1
+async first=-542398533 second=-542398533 issue_calls=1
-- 
2.54.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to