Commit e9908e5ee3 for ffmpeg
commit e9908e5ee388c54253669c5787fdc61cefdbfe50
Author: Romain Beauxis <romain.beauxis@gmail.com>
Date: Sun Nov 8 15:05:58 2026 -0600
avformat/ogg/mapping/theora: fix headers over 64 KiB
Pack the Theora headers into the extradata with Xiph lacing, as
vorbis_header() does, so that headers of any size fit.
The decoder already reads that layout, so oggtheora-demux only sees
the extradata change size.
diff --git a/libavformat/ogg/mapping/theora.c b/libavformat/ogg/mapping/theora.c
index 66905823f7..dae53be2ee 100644
--- a/libavformat/ogg/mapping/theora.c
+++ b/libavformat/ogg/mapping/theora.c
@@ -35,17 +35,48 @@ typedef struct TheoraParams {
int gpshift;
int gpmask;
unsigned version;
+ uint8_t *packet[3]; ///< headers of the link, until all three are in
+ int len[3];
} TheoraParams;
+/**
+ * Pack the three headers of a link with Xiph lacing, which carries headers
+ * of any size, and free them.
+ */
+static int theora_pack_headers(TheoraParams *thp, uint8_t **buf)
+{
+ uint64_t size = 1 + thp->len[0] / 255 + 1 + thp->len[1] / 255 + 1 +
+ (uint64_t)thp->len[0] + thp->len[1] + thp->len[2];
+ int offset;
+ uint8_t *ptr;
+
+ if (size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE)
+ return AVERROR_INVALIDDATA;
+
+ ptr = *buf = av_mallocz(size + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!ptr)
+ return AVERROR(ENOMEM);
+
+ ptr[0] = 2;
+ offset = 1;
+ offset += av_xiphlacing(&ptr[offset], thp->len[0]);
+ offset += av_xiphlacing(&ptr[offset], thp->len[1]);
+ for (int i = 0; i < 3; i++) {
+ memcpy(&ptr[offset], thp->packet[i], thp->len[i]);
+ offset += thp->len[i];
+ av_freep(&thp->packet[i]);
+ }
+ return offset;
+}
+
static int theora_header(AVFormatContext *s, int idx)
{
struct ogg *ogg = s->priv_data;
struct ogg_stream *os = ogg->streams + idx;
AVStream *st = s->streams[idx];
TheoraParams *thp = os->private;
- int cds = st->codecpar->extradata_size + os->psize + 2;
- int err;
- uint8_t *cdp;
+ int type = os->buf[os->pstart] & 0x7f;
+ int ret;
if (!(os->buf[os->pstart] & 0x80))
return 0;
@@ -129,18 +160,25 @@ static int theora_header(AVFormatContext *s, int idx)
return AVERROR_INVALIDDATA;
}
- if ((err = av_reallocp(&st->codecpar->extradata,
- cds + AV_INPUT_BUFFER_PADDING_SIZE)) < 0) {
- st->codecpar->extradata_size = 0;
- return err;
+ /* The headers are freed once packed, so a repeated one is ignored. */
+ if (type > 0 && !thp->packet[0] || type > 1 && !thp->packet[1])
+ return 1;
+
+ ret = av_reallocp(&thp->packet[type], os->psize);
+ if (ret < 0)
+ return ret;
+ memcpy(thp->packet[type], os->buf + os->pstart, os->psize);
+ thp->len[type] = os->psize;
+
+ if (type == 2) {
+ av_freep(&st->codecpar->extradata);
+ ret = theora_pack_headers(thp, &st->codecpar->extradata);
+ if (ret < 0) {
+ st->codecpar->extradata_size = 0;
+ return ret;
+ }
+ st->codecpar->extradata_size = ret;
}
- memset(st->codecpar->extradata + cds, 0, AV_INPUT_BUFFER_PADDING_SIZE);
-
- cdp = st->codecpar->extradata + st->codecpar->extradata_size;
- *cdp++ = os->psize >> 8;
- *cdp++ = os->psize & 0xff;
- memcpy(cdp, os->buf + os->pstart, os->psize);
- st->codecpar->extradata_size = cds;
return 1;
}
@@ -211,11 +249,22 @@ static int theora_packet(AVFormatContext *s, int idx)
return 0;
}
+static void theora_cleanup(AVFormatContext *s, int idx)
+{
+ struct ogg *ogg = s->priv_data;
+ TheoraParams *thp = ogg->streams[idx].private;
+
+ if (thp)
+ for (int i = 0; i < 3; i++)
+ av_freep(&thp->packet[i]);
+}
+
const struct ogg_codec ff_theora_codec = {
.magic = "\200theora",
.magicsize = 7,
.header = theora_header,
.packet = theora_packet,
.gptopts = theora_gptopts,
+ .cleanup = theora_cleanup,
.nb_header = 3,
};
diff --git a/tests/ref/fate/oggtheora-demux b/tests/ref/fate/oggtheora-demux
index 05a96c42ea..07cf941e50 100644
--- a/tests/ref/fate/oggtheora-demux
+++ b/tests/ref/fate/oggtheora-demux
@@ -1,4 +1,4 @@
-#extradata 0: 3299, 0x4acd0d5a
+#extradata 0: 3296, 0x2d280ccc
#tb 0: 1/25
#media_type 0: video
#codec_id 0: theora