Commit f9110f2a49 for ffmpeg

commit f9110f2a491ffb9b8de8306199707b1292633b93
Author: Kacper Michajłow <kasper93@gmail.com>
Date:   Tue Sep 8 05:51:42 2026 +0200

    avcodec/h264dec: decode late SEI before finishing frame threading setup

    SEI NAL units following the last VCL NAL unit start a new access unit,
    but some files keep them in the same packet as the preceding picture,
    e.g. YouTube encodes carry an unregistered user data SEI after the slice
    data. With frame threading such SEI was skipped and a "Late SEI is not
    implemented" warning was printed for every affected packet, because the
    SEI context is copied to the next frame thread in
    ff_h264_update_thread_context() and must not be modified after
    ff_thread_finish_setup(). Single threaded decoding decoded the SEI and
    kept its persistent state, like the alternative transfer characteristics
    or the x264 build, for the following pictures.

    Decode such SEI right before finishing the setup instead. The current
    picture is already set up at this point, so the SEI does not apply to
    it, matching the single threaded behavior.

    Fixes: https://code.ffmpeg.org/FFmpeg/FFmpeg/issues/21348
    Fixes: https://github.com/mpv-player/mpv/issues/18104
    Signed-off-by: Kacper Michajłow <kasper93@gmail.com>

diff --git a/libavcodec/h264dec.c b/libavcodec/h264dec.c
index 9bb5999005..597bdae548 100644
--- a/libavcodec/h264dec.c
+++ b/libavcodec/h264dec.c
@@ -603,6 +603,39 @@ static int h264_attach_partitions(const H264Context *h, H264SliceContext *sl,
     return idx;
 }

+static int h264_decode_sei(H264Context *h, H2645NAL *nal)
+{
+    AVCodecContext *const avctx = h->avctx;
+    int ret;
+
+    ret = ff_h264_sei_decode(&h->sei, &nal->gb, &h->ps, avctx);
+    h->has_recovery_point = h->has_recovery_point || h->sei.recovery_point.recovery_frame_cnt != -1;
+    if (avctx->debug & FF_DEBUG_GREEN_MD)
+        debug_green_metadata(&h->sei.green_metadata, avctx);
+    if (ret < 0 && (avctx->err_recognition & AV_EF_EXPLODE))
+        return ret;
+
+    return 0;
+}
+
+/* Decode the SEI NAL units following the slice at idx. */
+static int h264_decode_late_sei(H264Context *h, int idx)
+{
+    for (int i = idx + 1; i < h->pkt.nb_nals; i++) {
+        H2645NAL *nal = &h->pkt.nals[i];
+        int ret;
+
+        if (nal->type != H264_NAL_SEI)
+            continue;
+
+        ret = h264_decode_sei(h, nal);
+        if (ret < 0)
+            return ret;
+    }
+
+    return 0;
+}
+
 static int decode_nal_units(H264Context *h, AVBufferRef *buf_ref,
                             const uint8_t *buf, int buf_size)
 {
@@ -707,6 +740,9 @@ static int decode_nal_units(H264Context *h, AVBufferRef *buf_ref,
             if (h->current_slice == 1) {
                 if (avctx->active_thread_type & FF_THREAD_FRAME &&
                     i >= nals_needed && !h->setup_finished && h->cur_pic_ptr) {
+                    ret = h264_decode_late_sei(h, i);
+                    if (ret < 0)
+                        goto end;
                     ff_thread_finish_setup(avctx);
                     h->setup_finished = 1;
                 }
@@ -737,15 +773,11 @@ static int decode_nal_units(H264Context *h, AVBufferRef *buf_ref,
                        nal->type == H264_NAL_DPB ? 'B' : 'C');
             break;
         case H264_NAL_SEI:
-            if (h->setup_finished) {
-                avpriv_request_sample(avctx, "Late SEI");
+            /* already decoded by h264_decode_late_sei() */
+            if (h->setup_finished)
                 break;
-            }
-            ret = ff_h264_sei_decode(&h->sei, &nal->gb, &h->ps, avctx);
-            h->has_recovery_point = h->has_recovery_point || h->sei.recovery_point.recovery_frame_cnt != -1;
-            if (avctx->debug & FF_DEBUG_GREEN_MD)
-                debug_green_metadata(&h->sei.green_metadata, h->avctx);
-            if (ret < 0 && (h->avctx->err_recognition & AV_EF_EXPLODE))
+            ret = h264_decode_sei(h, nal);
+            if (ret < 0)
                 goto end;
             break;
         case H264_NAL_SPS: {