Commit 981fb1d790 for ffmpeg

commit 981fb1d790fc866335c6378cd0cc4b0e0ee71a20
Author: Romain Beauxis <romain.beauxis@gmail.com>
Date:   Sat Oct 31 09:55:33 2026 -0500

    avcodec/vp3: take new Theora headers from packet side data

    A chained Ogg Theora link has its own headers and starts a new
    logical stream, which the decoder only takes up from header packets
    and without frame threading.

    Set the decoder up again from headers passed as
    AV_PKT_DATA_NEW_EXTRADATA before decoding the packet that carries them,
    building its own coefficient VLCs rather than taking the first thread's.

diff --git a/libavcodec/vp3.c b/libavcodec/vp3.c
index 71e28184d2..70218c1a11 100644
--- a/libavcodec/vp3.c
+++ b/libavcodec/vp3.c
@@ -94,6 +94,8 @@ typedef struct Vp3Fragment {

 static int theora_decode_header(AVCodecContext *avctx, GetBitContext *gb);
 static int theora_decode_tables(AVCodecContext *avctx, GetBitContext *gb);
+static int theora_reinit(AVCodecContext *avctx, uint8_t *headers,
+                         int headers_size);


 /* There are 6 preset schemes, plus a free-form scheme */
@@ -302,6 +304,13 @@ typedef struct Vp3DecodeContext {
      */
     CoeffVLCs *coeff_vlc;

+    /**
+     * Headers taken from packet side data, NULL while the codec extradata
+     * applies; a RefStruct reference, padded for the bit reader.
+     */
+    uint8_t *headers;
+    int headers_size;
+
     /* these arrays need to be on 16-byte boundaries since SSE2 operations
      * index into them */
     DECLARE_ALIGNED(16, int16_t, qmat)[3][2][3][64];     ///< qmat[qpi][is_inter][plane]
@@ -369,6 +378,7 @@ static av_cold int vp3_decode_end(AVCodecContext *avctx)
     vp3_decode_flush(avctx);

     av_refstruct_unref(&s->coeff_vlc);
+    av_refstruct_unref(&s->headers);

     return 0;
 }
@@ -2453,7 +2463,9 @@ static av_cold int vp3_decode_init(AVCodecContext *avctx)
         }
     }

-    if (ff_thread_sync_ref(avctx, offsetof(Vp3DecodeContext, coeff_vlc)) != FF_THREAD_IS_COPY) {
+    /* The first thread's tables may be another link's than these headers'. */
+    if (s->headers ||
+        ff_thread_sync_ref(avctx, offsetof(Vp3DecodeContext, coeff_vlc)) != FF_THREAD_IS_COPY) {
         CoeffVLCs *vlcs = av_refstruct_alloc_ext(sizeof(*s->coeff_vlc), 0,
                                                  NULL, free_vlc_tables);
         if (!vlcs)
@@ -2542,6 +2554,30 @@ static int vp3_decode_frame(AVCodecContext *avctx, AVFrame *frame,
     GetBitContext gb;
     int ret;

+#if CONFIG_THEORA_DECODER
+    if (s->theora) {
+        size_t new_headers_size;
+        const uint8_t *new_headers = av_packet_get_side_data(avpkt,
+                                                             AV_PKT_DATA_NEW_EXTRADATA,
+                                                             &new_headers_size);
+        if (new_headers) {
+            uint8_t *headers;
+
+            if (new_headers_size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE)
+                return AVERROR_INVALIDDATA;
+            headers = av_refstruct_allocz(new_headers_size +
+                                          AV_INPUT_BUFFER_PADDING_SIZE);
+            if (!headers)
+                return AVERROR(ENOMEM);
+            memcpy(headers, new_headers, new_headers_size);
+            ret = theora_reinit(avctx, headers, new_headers_size);
+            av_refstruct_unref(&headers);
+            if (ret < 0)
+                return ret;
+        }
+    }
+#endif
+
     if ((ret = init_get_bits8(&gb, buf, buf_size)) < 0)
         return ret;

@@ -3131,6 +3167,26 @@ static av_cold int theora_decode_init(AVCodecContext *avctx)
                                     avctx->extradata_size);
 }

+/**
+ * Set the decoder up again from the headers of a new logical stream, taking
+ * a reference to them.
+ */
+static int theora_reinit(AVCodecContext *avctx, uint8_t *headers,
+                         int headers_size)
+{
+    Vp3DecodeContext *s = avctx->priv_data;
+    int ret;
+
+    vp3_decode_end(avctx);
+    av_refstruct_replace(&s->headers, headers);
+    s->headers_size = headers_size;
+    ret = theora_init_from_headers(avctx, s->headers, s->headers_size);
+    if (ret < 0)
+        vp3_decode_end(avctx);
+
+    return ret;
+}
+
 const FFCodec ff_theora_decoder = {
     .p.name                = "theora",
     CODEC_LONG_NAME("Theora"),