[PR] fftools/ffmpeg_sched: do not wait for unchoke to drain a decoder's overflow queue (PR #23870)

michaelni via ffmpeg-devel <[email protected]> Wed, 22 Jul 2026 01:30:29 -0000
Newsgroups gmane.comp.video.ffmpeg.devel
Message-ID <178468383021.59.11300423250385229993@29965ddac10e>
PR #23870 opened by michaelni
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23870
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23870.patch

Once tq_receive() on the decoder's input queue returns EOF, the packets
stashed in the overflow FIFO are the decoder's only remaining input, and
draining them is required for the downstream filtergraph, encoders and
muxer to terminate. Waiting to be unchoked here can deadlock:
schedule_update_locked() resolves the "still allow decoders to drain"
unchoke of finished output streams through fg->best_input, so when the
filtergraph currently requests an input whose upstream nodes have already
terminated - observed with -t, where the output trim filters propagate
EOF backwards, closing the filtergraph input without the filter thread's
choose_input() learning it is dead - no schedule update ever unchokes
this decoder and every remaining thread waits forever.

The drain needs no choking for rate control: each decoded frame is sent
through the bounded frame queue to the filtergraph, which provides
backpressure.

Observed as a rare hang on release/9.0 of
  ffmpeg -i issue3562_cut.avi -t 0.1 -bitexact \
    -filter_complex '[0:0]format=yuv420p;[0:1]aresample=osr=22050' \
    -y out.webm
(mjpeg+pcm avi) with the mjpeg decoder blocked in waiter_wait() called
from sch_dec_receive(), the demuxer, audio decoder and video encoder
threads already exited, and the filtergraph, audio encoder and muxer
threads blocked in tq_receive(). With each ffmpeg instance pinned to a
single CPU the hang reproduced in 110 of 3000 runs; with this change 0
of 3000 runs hang (Fisher exact p < 1e-30). fate-ffmpeg, trim and
shortest tests pass. The pending scheduler rework in PR #21241 removes
this wait in the same way.

Co-Authored-by: Fable-5



>From d440cfd3aaf248d7805deb2e140559960cbf248e Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Wed, 22 Jul 2026 01:05:44 +0200
Subject: [PATCH] fftools/ffmpeg_sched: do not wait for unchoke to drain a
 decoder's overflow queue

Once tq_receive() on the decoder's input queue returns EOF, the packets
stashed in the overflow FIFO are the decoder's only remaining input, and
draining them is required for the downstream filtergraph, encoders and
muxer to terminate. Waiting to be unchoked here can deadlock:
schedule_update_locked() resolves the "still allow decoders to drain"
unchoke of finished output streams through fg->best_input, so when the
filtergraph currently requests an input whose upstream nodes have already
terminated - observed with -t, where the output trim filters propagate
EOF backwards, closing the filtergraph input without the filter thread's
choose_input() learning it is dead - no schedule update ever unchokes
this decoder and every remaining thread waits forever.

The drain needs no choking for rate control: each decoded frame is sent
through the bounded frame queue to the filtergraph, which provides
backpressure.

Observed as a rare hang on release/9.0 of
  ffmpeg -i issue3562_cut.avi -t 0.1 -bitexact \
    -filter_complex '[0:0]format=yuv420p;[0:1]aresample=osr=22050' \
    -y out.webm
(mjpeg+pcm avi) with the mjpeg decoder blocked in waiter_wait() called
from sch_dec_receive(), the demuxer, audio decoder and video encoder
threads already exited, and the filtergraph, audio encoder and muxer
threads blocked in tq_receive(). With each ffmpeg instance pinned to a
single CPU the hang reproduced in 110 of 3000 runs; with this change 0
of 3000 runs hang (Fisher exact p < 1e-30). fate-ffmpeg, trim and
shortest tests pass. The pending scheduler rework in PR #21241 removes
this wait in the same way.

Co-Authored-by: Fable-5
---
 fftools/ffmpeg_sched.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/fftools/ffmpeg_sched.c b/fftools/ffmpeg_sched.c
index 8ec3bdaf80..e4affb0dd8 100644
--- a/fftools/ffmpeg_sched.c
+++ b/fftools/ffmpeg_sched.c
@@ -2360,11 +2360,11 @@ retry:
     ret = tq_receive(dec->queue, &dummy, pkt, 0);
     av_assert0(dummy <= 0);
 
-    // drain packets from overflow queue before returning EOF
+    // drain packets from the overflow queue before returning EOF; waiting
+    // to be unchoked here can deadlock, as the unchoke may never come once
+    // the filtergraph requests an input whose upstream has terminated, and
+    // the drain is rate-limited by the downstream frame queue anyway
     if (ret == AVERROR_EOF && av_container_fifo_can_read(dec->overflow)) {
-        int terminate = waiter_wait(sch, &dec->waiter);
-        if (terminate)
-            return ret;
         return av_container_fifo_read(dec->overflow, pkt, 0);
     } else if (ret < 0)
         return ret;
-- 
2.52.0

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