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

Draining an encoder before submitting any frame leaves `pic_end` NULL. The EOF 
timestamp fixup nevertheless dereferences it because `input_order <= 
decode_delay`. Require a picture before applying that correction, allowing the 
existing receive path to return EOF for empty input.

The guard was proposed by WindrunnerSky in #24248 and is attributed to that 
author. A separate commit adds a hardware-free FATE test for empty input and 
short streams around the decode-delay boundary; expected output resides in 
`tests/ref/fate/hw-base-encode`.

Fixes #24248.

Reproduced on master f68e1afc1b7c using an NVIDIA GeForce RTX 5090 with driver 
595.84:

    ffmpeg -init_hw_device vulkan=vk:0 -filter_hw_device vk \
        -f lavfi -i testsrc2=size=320x240:rate=30:duration=0.1 \
        -vf format=nv12,hwupload -c:v h264_vulkan -profile:v high \
        -t 0 -f null -

Before the fix this crashes in `hw_base_encode_send_frame()`; afterward it 
exits successfully with empty output.

Validation:

- H.264 and HEVC Vulkan empty-input cases with `-bf 0` and `-bf 2` pass after 
the fix.
- Nonempty 1-, 2-, 3-, and 12-frame cases produce identical framecrc output 
before and after the fix for both encoders and B-frame settings.
- `make fate-hw-base-encode` fails with SIGSEGV before the guard and passes 
afterward on x86-64.
- The FATE test also passes on ARM64 with hardware support disabled and only 
the `pcm_s16le` encoder enabled.

The equivalent VAAPI regression was verified at 5fdcf85bbffe against its 
immediate parent using an isolated empty-input handler test. Active release 
branches also retain the unguarded expression; this PR targets master.


>From 019c946a71cbee63e43c9ad74e06ef30659091ea Mon Sep 17 00:00:00 2001
From: WindrunnerSky <>
Date: Fri, 2 Oct 2026 11:47:02 +0000
Subject: [PATCH 1/2] avcodec/hw_base_encode: guard timestamp fixup for empty
 input

Draining an encoder before any frame has been submitted leaves pic_end NULL. 
Skip the timestamp fixup in that case; the receive path will then return EOF 
without producing a packet.

Fixes: https://code.ffmpeg.org/FFmpeg/FFmpeg/issues/24248

Assisted-by: Fairy
---
 libavcodec/hw_base_encode.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavcodec/hw_base_encode.c b/libavcodec/hw_base_encode.c
index 307e76ed79..5ba194fab5 100644
--- a/libavcodec/hw_base_encode.c
+++ b/libavcodec/hw_base_encode.c
@@ -508,7 +508,7 @@ static int hw_base_encode_send_frame(AVCodecContext *avctx, 
FFHWBaseEncodeContex
 
         // Fix timestamps if we hit end-of-stream before the initial decode
         // delay has elapsed.
-        if (ctx->input_order <= ctx->decode_delay)
+        if (ctx->input_order <= ctx->decode_delay && ctx->pic_end)
             ctx->dts_pts_diff = ctx->pic_end->pts - ctx->first_pts;
     }
 
-- 
2.52.0


>From 0d3c4d74762d416f97fa783f84c7a6931c04b551 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Fri, 2 Oct 2026 11:47:02 +0000
Subject: [PATCH 2/2] avcodec/tests: cover hardware encoder timestamp fixup at
 EOF

Exercise empty input and short streams around the decode delay without 
requiring hardware. Print results for FATE comparison, including the EOF state 
and timestamp difference.

Assisted-by: Fairy
---
 libavcodec/Makefile               |  1 +
 libavcodec/tests/hw_base_encode.c | 49 +++++++++++++++++++++++++++++++
 tests/fate/libavcodec.mak         |  4 +++
 tests/ref/fate/hw-base-encode     | 20 +++++++++++++
 4 files changed, 74 insertions(+)
 create mode 100644 libavcodec/tests/hw_base_encode.c
 create mode 100644 tests/ref/fate/hw-base-encode

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 5ee0c00bc6..59e86e163b 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1391,6 +1391,7 @@ TESTPROGS-$(CONFIG_APV_DECODER)           += apv
 TESTPROGS-$(CONFIG_AV1_VAAPI_ENCODER)     += av1_levels
 TESTPROGS-$(CONFIG_CABAC)                 += cabac
 TESTPROGS-$(CONFIG_CELP_MATH)             += celp_math
+TESTPROGS-$(CONFIG_ENCODERS)              += hw_base_encode
 TESTPROGS-$(CONFIG_GOLOMB)                += golomb
 TESTPROGS-$(CONFIG_IDCTDSP)               += dct
 TESTPROGS-$(CONFIG_DXV_ENCODER)           += hashtable
diff --git a/libavcodec/tests/hw_base_encode.c 
b/libavcodec/tests/hw_base_encode.c
new file mode 100644
index 0000000000..578a97919b
--- /dev/null
+++ b/libavcodec/tests/hw_base_encode.c
@@ -0,0 +1,49 @@
+/*
+ * 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 "libavcodec/hw_base_encode.c"
+
+int main(void)
+{
+    AVCodecContext avctx = { 0 };
+
+    for (int delay = 0; delay <= 3; delay++) {
+        for (int count = 0; count <= 4; count++) {
+            FFHWBaseEncodePicture pic = { 0 };
+            FFHWBaseEncodeContext ctx = { 0 };
+            int ret;
+
+            ctx.decode_delay = delay;
+            ctx.input_order = count;
+            ctx.first_pts = 100;
+            if (count) {
+                pic.pts = ctx.first_pts + 10 * (count - 1);
+                ctx.pic_end = &pic;
+            }
+            if (count > delay)
+                ctx.dts_pts_diff = 10 * delay;
+
+            ret = hw_base_encode_send_frame(&avctx, &ctx, NULL);
+            printf("delay=%d frames=%d return=%d eof=%d pts_diff=%"PRId64"\n",
+                   delay, count, ret, ctx.end_of_stream, ctx.dts_pts_diff);
+        }
+    }
+    return 0;
+}
diff --git a/tests/fate/libavcodec.mak b/tests/fate/libavcodec.mak
index 3cde003212..d75739d336 100644
--- a/tests/fate/libavcodec.mak
+++ b/tests/fate/libavcodec.mak
@@ -52,6 +52,10 @@ fate-hashtable: libavcodec/tests/hashtable$(EXESUF)
 fate-hashtable: CMD = run libavcodec/tests/hashtable$(EXESUF)
 fate-hashtable: CMP = null
 
+FATE_LIBAVCODEC-$(CONFIG_ENCODERS) += fate-hw-base-encode
+fate-hw-base-encode: libavcodec/tests/hw_base_encode$(EXESUF)
+fate-hw-base-encode: CMD = run libavcodec/tests/hw_base_encode$(EXESUF)
+
 FATE_LIBAVCODEC-$(CONFIG_IDCTDSP) += fate-idct8x8-0 fate-idct8x8-1 
fate-idct8x8-2 fate-idct248
 
 fate-idct8x8-0: libavcodec/tests/dct$(EXESUF)
diff --git a/tests/ref/fate/hw-base-encode b/tests/ref/fate/hw-base-encode
new file mode 100644
index 0000000000..9f0725473f
--- /dev/null
+++ b/tests/ref/fate/hw-base-encode
@@ -0,0 +1,20 @@
+delay=0 frames=0 return=0 eof=1 pts_diff=0
+delay=0 frames=1 return=0 eof=1 pts_diff=0
+delay=0 frames=2 return=0 eof=1 pts_diff=0
+delay=0 frames=3 return=0 eof=1 pts_diff=0
+delay=0 frames=4 return=0 eof=1 pts_diff=0
+delay=1 frames=0 return=0 eof=1 pts_diff=0
+delay=1 frames=1 return=0 eof=1 pts_diff=0
+delay=1 frames=2 return=0 eof=1 pts_diff=10
+delay=1 frames=3 return=0 eof=1 pts_diff=10
+delay=1 frames=4 return=0 eof=1 pts_diff=10
+delay=2 frames=0 return=0 eof=1 pts_diff=0
+delay=2 frames=1 return=0 eof=1 pts_diff=0
+delay=2 frames=2 return=0 eof=1 pts_diff=10
+delay=2 frames=3 return=0 eof=1 pts_diff=20
+delay=2 frames=4 return=0 eof=1 pts_diff=20
+delay=3 frames=0 return=0 eof=1 pts_diff=0
+delay=3 frames=1 return=0 eof=1 pts_diff=0
+delay=3 frames=2 return=0 eof=1 pts_diff=10
+delay=3 frames=3 return=0 eof=1 pts_diff=20
+delay=3 frames=4 return=0 eof=1 pts_diff=30
-- 
2.52.0

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

Reply via email to