From 0d1292ea99bc4e5fa2da438259fa01a2374e3e04 Mon Sep 17 00:00:00 2001 From: Markus Fritsche Date: Fri, 22 May 2026 14:18:25 +0200 Subject: [PATCH] avcodec/h264: restore AV_CODEC_FLAG_LOW_DELAY semantics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FFmpeg 8.x dropped the H.264 decoder's low_delay path — AV_CODEC_FLAG_LOW_DELAY no longer prevents h264_select_output_frame from running the display-order DPB output queue. V4L2-stateless-style consumers (daedalus-v4l2 daemon, libva-v4l2-request-fourier) that set the flag end up seeing the 2-1-4-3 pair-swap pattern on B-frame streams again. Restore the documented semantics: - Early-exit at the top of h264_select_output_frame when the flag is set: emit the just-decoded picture immediately as next_output_pic, mirror the corruption / recovery-point tracking the main path performs, and skip the entire delayed_pic[] / POC reorder machinery. - Suppress the SPS-driven has_b_frames clobber in h264_field_start when the flag is set, so the per-slice bitstream_restriction_flag re-pickup cannot reintroduce a nonzero reorder buffer mid-stream. This is a fork-only change required by the daedalus-v4l2 daemon's one-frame-per-send_packet contract; upstream FFmpeg consumers that expect display-order output remain untouched (flag default = off). Refs reauktion/daedalus-v4l2#11 — substitution arc step 2 deblock + flag-restoration follow-up. --- libavcodec/h264_slice.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/libavcodec/h264_slice.c b/libavcodec/h264_slice.c index 97fab70..a7bfbd6 100644 --- a/libavcodec/h264_slice.c +++ b/libavcodec/h264_slice.c @@ -1308,6 +1308,28 @@ static int h264_select_output_frame(H264Context *h) cur->mmco_reset = h->mmco_reset; h->mmco_reset = 0; + /* AV_CODEC_FLAG_LOW_DELAY restore (FFmpeg 8.x dropped the H.264 + * decoder's low_delay path). Bypass the display-order DPB + * output queue: emit the just-decoded picture immediately, in + * decode order, one per send_packet. V4L2-stateless-style + * consumers (daedalus-v4l2 daemon, libva-v4l2-request-fourier) + * do their own POC-based reorder downstream and require this + * behaviour. */ + if (h->avctx->flags & AV_CODEC_FLAG_LOW_DELAY) { + h->next_output_pic = cur; + h->next_outputed_poc = cur->poc; + h->frame_recovered |= cur->recovered; + cur->recovered |= h->frame_recovered & FRAME_RECOVERED_SEI; + if (!cur->recovered) { + if (!(h->avctx->flags & AV_CODEC_FLAG_OUTPUT_CORRUPT) && + !(h->avctx->flags2 & AV_CODEC_FLAG2_SHOW_ALL)) + h->next_output_pic = NULL; + else + cur->f->flags |= AV_FRAME_FLAG_CORRUPT; + } + return 0; + } + if (sps->bitstream_restriction_flag || h->avctx->strict_std_compliance >= FF_COMPLIANCE_STRICT) { h->avctx->has_b_frames = FFMAX(h->avctx->has_b_frames, sps->num_reorder_frames); @@ -1415,6 +1437,7 @@ static int h264_field_start(H264Context *h, const H264SliceContext *sl, sps = h->ps.sps; if (sps->bitstream_restriction_flag && + !(h->avctx->flags & AV_CODEC_FLAG_LOW_DELAY) && h->avctx->has_b_frames < sps->num_reorder_frames) { h->avctx->has_b_frames = sps->num_reorder_frames; } -- 2.47.3