Commit 111ff3f736 for ffmpeg
commit 111ff3f736d238ba3ffd01361820e9121cdfd2c8
Author: Romain Beauxis <romain.beauxis@gmail.com>
Date: Thu Nov 5 10:15:42 2026 -0600
avformat/vorbis/comment: move the Vorbis comment reader out of the Ogg demuxer
The reader moves from oggparsevorbis.c to vorbis/comment/reader.c and its declarations to vorbis/comment.h, next to the writer.
The FLAC and Matroska demuxers link the reader instead of the Ogg Vorbis mapping.
diff --git a/.forgejo/CODEOWNERS b/.forgejo/CODEOWNERS
index 380cb4f797..b07b4617be 100644
--- a/.forgejo/CODEOWNERS
+++ b/.forgejo/CODEOWNERS
@@ -170,6 +170,7 @@ libavformat/rm.* @pross
libavformat/sauce.* @pross
libavformat/scd.* @zane
libavformat/tty.* @pross
+libavformat/vorbis/comment/reader[.]c @toots
libavformat/whip.c @JackLau
libavformat/wsd.* @pross
libavformat/wtv.* @pross
diff --git a/MAINTAINERS b/MAINTAINERS
index ef62381933..dfba5da3d4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -506,6 +506,7 @@ Muxers/Demuxers:
tta.c Alex Beregszaszi
txd.c Ivo van Poorten
voc.c Aurelien Jacobs
+ vorbis/comment/reader.c David Conrad, Romain Beauxis
wav.c Michael Niedermayer
wc3movie.c Mike Melanson
webm dash (matroskaenc.c) Vignesh Venkatasubramanian
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 044eab9534..29db52477f 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -227,8 +227,8 @@ OBJS-$(CONFIG_FITS_DEMUXER) += fitsdec.o
OBJS-$(CONFIG_FITS_MUXER) += fitsenc.o
OBJS-$(CONFIG_FLAC_DEMUXER) += flacdec.o rawdec.o \
flac_picture.o \
- oggparsevorbis.o \
replaygain.o \
+ vorbis/comment/reader.o \
vorbis/comment/writer.o
OBJS-$(CONFIG_FLAC_MUXER) += flacenc.o flacenc_header.o \
vorbis/comment/writer.o
@@ -373,7 +373,7 @@ OBJS-$(CONFIG_M4V_DEMUXER) += m4vdec.o rawdec.o
OBJS-$(CONFIG_M4V_MUXER) += rawenc.o
OBJS-$(CONFIG_MATROSKA_DEMUXER) += matroskadec.o matroska.o \
flac_picture.o rmsipr.o \
- oggparsevorbis.o vorbis/comment/writer.o \
+ vorbis/comment/reader.o vorbis/comment/writer.o \
qtpalette.o replaygain.o dovi_isom.o
OBJS-$(CONFIG_MATROSKA_MUXER) += matroskaenc.o matroska.o \
flacenc_header.o vorbis/comment/writer.o \
@@ -457,7 +457,8 @@ OBJS-$(CONFIG_OGG_DEMUXER) += oggdec.o \
oggparsevorbis.o \
oggparsevp8.o \
replaygain.o \
- vorbis/comment/writer.o \
+ vorbis/comment/reader.o \
+ vorbis/comment/writer.o \
flac_picture.o
OBJS-$(CONFIG_OGA_MUXER) += oggenc.o \
vorbis/comment/writer.o
diff --git a/libavformat/flacdec.c b/libavformat/flacdec.c
index 1f5cfeed71..a07c8bacba 100644
--- a/libavformat/flacdec.c
+++ b/libavformat/flacdec.c
@@ -30,7 +30,7 @@
#include "flac_picture.h"
#include "internal.h"
#include "rawdec.h"
-#include "oggdec.h"
+#include "libavformat/vorbis/comment.h"
#include "replaygain.h"
#define SEEKPOINT_SIZE 18
diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c
index 706a0566bc..2aa4f83e78 100644
--- a/libavformat/matroskadec.c
+++ b/libavformat/matroskadec.c
@@ -66,7 +66,7 @@
#include "internal.h"
#include "isom.h"
#include "matroska.h"
-#include "oggdec.h"
+#include "libavformat/vorbis/comment.h"
/* For ff_codec_get_id(). */
#include "riff.h"
#include "rmsipr.h"
diff --git a/libavformat/oggdec.h b/libavformat/oggdec.h
index b7b008cc1f..107ba12f02 100644
--- a/libavformat/oggdec.h
+++ b/libavformat/oggdec.h
@@ -139,28 +139,6 @@ extern const struct ogg_codec ff_theora_codec;
extern const struct ogg_codec ff_vorbis_codec;
extern const struct ogg_codec ff_vp8_codec;
-/**
- * Parse Vorbis comments
- *
- * @note The buffer will be temporarily modified by this function,
- * so it needs to be writable. Furthermore it must be padded
- * by a single byte (not counted in size).
- * All changes will have been reverted upon return.
- */
-int ff_vorbis_comment(AVFormatContext *ms, AVDictionary **m,
- const uint8_t *buf, int size, int parse_picture);
-
-/**
- * Parse Vorbis comments and add metadata to an AVStream
- *
- * @note The buffer will be temporarily modified by this function,
- * so it needs to be writable. Furthermore it must be padded
- * by a single byte (not counted in size).
- * All changes will have been reverted upon return.
- */
-int ff_vorbis_stream_comment(AVFormatContext *as, AVStream *st,
- const uint8_t *buf, int size);
-
/**
* Parse Vorbis comments, add metadata to an AVStream
*
diff --git a/libavformat/oggparseflac.c b/libavformat/oggparseflac.c
index 31dd1439da..9926c5ebbb 100644
--- a/libavformat/oggparseflac.c
+++ b/libavformat/oggparseflac.c
@@ -26,6 +26,7 @@
#include "avformat.h"
#include "internal.h"
#include "oggdec.h"
+#include "libavformat/vorbis/comment.h"
#define OGG_FLAC_METADATA_TYPE_STREAMINFO 0x7F
#define OGG_FLAC_MAGIC "\177FLAC"
diff --git a/libavformat/oggparseogm.c b/libavformat/oggparseogm.c
index 1e794745ab..371a05a2c6 100644
--- a/libavformat/oggparseogm.c
+++ b/libavformat/oggparseogm.c
@@ -31,6 +31,7 @@
#include "avformat.h"
#include "internal.h"
#include "oggdec.h"
+#include "libavformat/vorbis/comment.h"
#include "riff.h"
static int
diff --git a/libavformat/oggparseopus.c b/libavformat/oggparseopus.c
index 6dde828abf..a9ef988c45 100644
--- a/libavformat/oggparseopus.c
+++ b/libavformat/oggparseopus.c
@@ -26,6 +26,7 @@
#include "avformat.h"
#include "internal.h"
#include "oggdec.h"
+#include "libavformat/vorbis/comment.h"
struct oggopus_private {
int need_comments;
diff --git a/libavformat/oggparsespeex.c b/libavformat/oggparsespeex.c
index 0a4b32de15..79ec881835 100644
--- a/libavformat/oggparsespeex.c
+++ b/libavformat/oggparsespeex.c
@@ -29,6 +29,7 @@
#include "avformat.h"
#include "internal.h"
#include "oggdec.h"
+#include "libavformat/vorbis/comment.h"
struct speex_params {
int packet_size;
diff --git a/libavformat/oggparsetheora.c b/libavformat/oggparsetheora.c
index e104d63229..c0ddbff8a3 100644
--- a/libavformat/oggparsetheora.c
+++ b/libavformat/oggparsetheora.c
@@ -29,6 +29,7 @@
#include "avformat.h"
#include "internal.h"
#include "oggdec.h"
+#include "libavformat/vorbis/comment.h"
typedef struct TheoraParams {
int gpshift;
diff --git a/libavformat/oggparsevorbis.c b/libavformat/oggparsevorbis.c
index f00dbfb517..d50a8e01f7 100644
--- a/libavformat/oggparsevorbis.c
+++ b/libavformat/oggparsevorbis.c
@@ -24,8 +24,6 @@
#include <stdlib.h>
-#include "libavutil/avstring.h"
-#include "libavutil/base64.h"
#include "libavutil/dict.h"
#include "libavutil/mem.h"
@@ -34,166 +32,11 @@
#include "avformat.h"
#include "demux.h"
-#include "flac_picture.h"
#include "internal.h"
#include "oggdec.h"
#include "libavformat/vorbis/comment.h"
#include "replaygain.h"
-static int ogm_chapter(AVFormatContext *as, const uint8_t *key, const uint8_t *val)
-{
- int i, cnum, h, m, s, ms, keylen = strlen(key);
- AVChapter *chapter = NULL;
-
- if (keylen < 9 || av_strncasecmp(key, "CHAPTER", 7) || sscanf(key+7, "%03d", &cnum) != 1)
- return 0;
-
- if (keylen <= 10) {
- if (sscanf(val, "%02d:%02d:%02d.%03d", &h, &m, &s, &ms) < 4)
- return 0;
-
- avpriv_new_chapter(as, cnum, (AVRational) { 1, 1000 },
- ms + 1000 * (s + 60 * (m + 60 * h)),
- AV_NOPTS_VALUE, NULL);
- } else if (!av_strcasecmp(key + keylen - 4, "NAME")) {
- for (i = 0; i < as->nb_chapters; i++)
- if (as->chapters[i]->id == cnum) {
- chapter = as->chapters[i];
- break;
- }
- if (!chapter)
- return 0;
-
- av_dict_set(&chapter->metadata, "title", val, 0);
- } else
- return 0;
-
- return 1;
-}
-
-int ff_vorbis_stream_comment(AVFormatContext *as, AVStream *st,
- const uint8_t *buf, int size)
-{
- int updates = ff_vorbis_comment(as, &st->metadata, buf, size, 1);
-
- if (updates > 0) {
- st->event_flags |= AVSTREAM_EVENT_FLAG_METADATA_UPDATED;
- }
-
- return updates;
-}
-
-/**
- * This function temporarily modifies the (const qualified) input buffer
- * and reverts its changes before return. The input buffer needs to have
- * at least one byte of padding.
- */
-static int vorbis_parse_single_comment(AVFormatContext *as, AVDictionary **m,
- const uint8_t *buf, uint32_t size,
- int *updates, int parse_picture)
-{
- char *t = (char*)buf, *v = memchr(t, '=', size);
- int tl, vl;
- char backup;
-
- if (!v)
- return 0;
-
- tl = v - t;
- vl = size - tl - 1;
- v++;
-
- if (!tl || !vl)
- return 0;
-
- t[tl] = 0;
-
- backup = v[vl];
- v[vl] = 0;
-
- /* The format in which the pictures are stored is the FLAC format.
- * Xiph says: "The binary FLAC picture structure is base64 encoded
- * and placed within a VorbisComment with the tag name
- * 'METADATA_BLOCK_PICTURE'. This is the preferred and
- * recommended way of embedding cover art within VorbisComments."
- */
- if (!av_strcasecmp(t, "METADATA_BLOCK_PICTURE") && parse_picture) {
- int ret, len = AV_BASE64_DECODE_SIZE(vl);
- uint8_t *pict = av_malloc(len + AV_INPUT_BUFFER_PADDING_SIZE);
-
- if (!pict) {
- av_log(as, AV_LOG_WARNING, "out-of-memory error. Skipping cover art block.\n");
- goto end;
- }
- ret = av_base64_decode(pict, v, len);
- if (ret > 0)
- ret = ff_flac_parse_picture(as, &pict, ret, 0);
- av_freep(&pict);
- if (ret < 0) {
- av_log(as, AV_LOG_WARNING, "Failed to parse cover art block.\n");
- goto end;
- }
- } else if (!ogm_chapter(as, t, v)) {
- (*updates)++;
- if (av_dict_get(*m, t, NULL, 0))
- av_dict_set(m, t, ";", AV_DICT_APPEND);
- av_dict_set(m, t, v, AV_DICT_APPEND);
- }
-end:
- t[tl] = '=';
- v[vl] = backup;
-
- return 0;
-}
-
-int ff_vorbis_comment(AVFormatContext *as, AVDictionary **m,
- const uint8_t *buf, int size,
- int parse_picture)
-{
- const uint8_t *p = buf;
- const uint8_t *end = buf + size;
- int updates = 0;
- unsigned n;
- int s, ret;
-
- /* must have vendor_length and user_comment_list_length */
- if (size < 8)
- return AVERROR_INVALIDDATA;
-
- s = bytestream_get_le32(&p);
-
- if (end - p - 4 < s || s < 0)
- return AVERROR_INVALIDDATA;
-
- p += s;
-
- n = bytestream_get_le32(&p);
-
- while (end - p >= 4 && n > 0) {
- s = bytestream_get_le32(&p);
-
- if (end - p < s || s < 0)
- break;
-
- ret = vorbis_parse_single_comment(as, m, p, s, &updates, parse_picture);
- if (ret < 0)
- return ret;
- p += s;
- n--;
- }
-
- if (p != end)
- av_log(as, AV_LOG_INFO,
- "%td bytes of comment header remain\n", end - p);
- if (n > 0)
- av_log(as, AV_LOG_INFO,
- "truncated comment header, %i comments not found\n", n);
-
- ff_metadata_conv(m, NULL, ff_vorbiscomment_metadata_conv);
-
- return updates;
-}
-
/*
* Parse the vorbis header
*
diff --git a/libavformat/oggparsevp8.c b/libavformat/oggparsevp8.c
index 676b3a0f8e..39ab927500 100644
--- a/libavformat/oggparsevp8.c
+++ b/libavformat/oggparsevp8.c
@@ -24,6 +24,7 @@
#include "avformat.h"
#include "internal.h"
#include "oggdec.h"
+#include "libavformat/vorbis/comment.h"
#define VP8_HEADER_SIZE 26
diff --git a/libavformat/vorbis/comment.h b/libavformat/vorbis/comment.h
index 2762d63135..2256632348 100644
--- a/libavformat/vorbis/comment.h
+++ b/libavformat/vorbis/comment.h
@@ -54,4 +54,26 @@ int ff_vorbiscomment_write(AVIOContext *pb, const AVDictionary *m,
extern const AVMetadataConv ff_vorbiscomment_metadata_conv[];
+/**
+ * Parse Vorbis comments
+ *
+ * @note The buffer will be temporarily modified by this function,
+ * so it needs to be writable. Furthermore it must be padded
+ * by a single byte (not counted in size).
+ * All changes will have been reverted upon return.
+ */
+int ff_vorbis_comment(AVFormatContext *ms, AVDictionary **m,
+ const uint8_t *buf, int size, int parse_picture);
+
+/**
+ * Parse Vorbis comments and add metadata to an AVStream
+ *
+ * @note The buffer will be temporarily modified by this function,
+ * so it needs to be writable. Furthermore it must be padded
+ * by a single byte (not counted in size).
+ * All changes will have been reverted upon return.
+ */
+int ff_vorbis_stream_comment(AVFormatContext *as, AVStream *st,
+ const uint8_t *buf, int size);
+
#endif /* AVFORMAT_VORBIS_COMMENT_H */
diff --git a/libavformat/vorbis/comment/reader.c b/libavformat/vorbis/comment/reader.c
new file mode 100644
index 0000000000..2e7215b9a8
--- /dev/null
+++ b/libavformat/vorbis/comment/reader.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2005 Michael Ahlberg, Måns Rullgård
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "libavutil/avstring.h"
+#include "libavutil/base64.h"
+#include "libavutil/dict.h"
+#include "libavutil/mem.h"
+
+#include "libavcodec/bytestream.h"
+
+#include "libavformat/avformat.h"
+#include "libavformat/demux.h"
+#include "libavformat/flac_picture.h"
+#include "libavformat/internal.h"
+#include "libavformat/metadata.h"
+#include "libavformat/vorbis/comment.h"
+
+static int ogm_chapter(AVFormatContext *as, const uint8_t *key, const uint8_t *val)
+{
+ int i, cnum, h, m, s, ms, keylen = strlen(key);
+ AVChapter *chapter = NULL;
+
+ if (keylen < 9 || av_strncasecmp(key, "CHAPTER", 7) || sscanf(key+7, "%03d", &cnum) != 1)
+ return 0;
+
+ if (keylen <= 10) {
+ if (sscanf(val, "%02d:%02d:%02d.%03d", &h, &m, &s, &ms) < 4)
+ return 0;
+
+ avpriv_new_chapter(as, cnum, (AVRational) { 1, 1000 },
+ ms + 1000 * (s + 60 * (m + 60 * h)),
+ AV_NOPTS_VALUE, NULL);
+ } else if (!av_strcasecmp(key + keylen - 4, "NAME")) {
+ for (i = 0; i < as->nb_chapters; i++)
+ if (as->chapters[i]->id == cnum) {
+ chapter = as->chapters[i];
+ break;
+ }
+ if (!chapter)
+ return 0;
+
+ av_dict_set(&chapter->metadata, "title", val, 0);
+ } else
+ return 0;
+
+ return 1;
+}
+
+int ff_vorbis_stream_comment(AVFormatContext *as, AVStream *st,
+ const uint8_t *buf, int size)
+{
+ int updates = ff_vorbis_comment(as, &st->metadata, buf, size, 1);
+
+ if (updates > 0) {
+ st->event_flags |= AVSTREAM_EVENT_FLAG_METADATA_UPDATED;
+ }
+
+ return updates;
+}
+
+/**
+ * This function temporarily modifies the (const qualified) input buffer
+ * and reverts its changes before return. The input buffer needs to have
+ * at least one byte of padding.
+ */
+static int vorbis_parse_single_comment(AVFormatContext *as, AVDictionary **m,
+ const uint8_t *buf, uint32_t size,
+ int *updates, int parse_picture)
+{
+ char *t = (char*)buf, *v = memchr(t, '=', size);
+ int tl, vl;
+ char backup;
+
+ if (!v)
+ return 0;
+
+ tl = v - t;
+ vl = size - tl - 1;
+ v++;
+
+ if (!tl || !vl)
+ return 0;
+
+ t[tl] = 0;
+
+ backup = v[vl];
+ v[vl] = 0;
+
+ /* The format in which the pictures are stored is the FLAC format.
+ * Xiph says: "The binary FLAC picture structure is base64 encoded
+ * and placed within a VorbisComment with the tag name
+ * 'METADATA_BLOCK_PICTURE'. This is the preferred and
+ * recommended way of embedding cover art within VorbisComments."
+ */
+ if (!av_strcasecmp(t, "METADATA_BLOCK_PICTURE") && parse_picture) {
+ int ret, len = AV_BASE64_DECODE_SIZE(vl);
+ uint8_t *pict = av_malloc(len + AV_INPUT_BUFFER_PADDING_SIZE);
+
+ if (!pict) {
+ av_log(as, AV_LOG_WARNING, "out-of-memory error. Skipping cover art block.\n");
+ goto end;
+ }
+ ret = av_base64_decode(pict, v, len);
+ if (ret > 0)
+ ret = ff_flac_parse_picture(as, &pict, ret, 0);
+ av_freep(&pict);
+ if (ret < 0) {
+ av_log(as, AV_LOG_WARNING, "Failed to parse cover art block.\n");
+ goto end;
+ }
+ } else if (!ogm_chapter(as, t, v)) {
+ (*updates)++;
+ if (av_dict_get(*m, t, NULL, 0))
+ av_dict_set(m, t, ";", AV_DICT_APPEND);
+ av_dict_set(m, t, v, AV_DICT_APPEND);
+ }
+end:
+ t[tl] = '=';
+ v[vl] = backup;
+
+ return 0;
+}
+
+int ff_vorbis_comment(AVFormatContext *as, AVDictionary **m,
+ const uint8_t *buf, int size,
+ int parse_picture)
+{
+ const uint8_t *p = buf;
+ const uint8_t *end = buf + size;
+ int updates = 0;
+ unsigned n;
+ int s, ret;
+
+ /* must have vendor_length and user_comment_list_length */
+ if (size < 8)
+ return AVERROR_INVALIDDATA;
+
+ s = bytestream_get_le32(&p);
+
+ if (end - p - 4 < s || s < 0)
+ return AVERROR_INVALIDDATA;
+
+ p += s;
+
+ n = bytestream_get_le32(&p);
+
+ while (end - p >= 4 && n > 0) {
+ s = bytestream_get_le32(&p);
+
+ if (end - p < s || s < 0)
+ break;
+
+ ret = vorbis_parse_single_comment(as, m, p, s, &updates, parse_picture);
+ if (ret < 0)
+ return ret;
+ p += s;
+ n--;
+ }
+
+ if (p != end)
+ av_log(as, AV_LOG_INFO,
+ "%td bytes of comment header remain\n", end - p);
+ if (n > 0)
+ av_log(as, AV_LOG_INFO,
+ "truncated comment header, %i comments not found\n", n);
+
+ ff_metadata_conv(m, NULL, ff_vorbiscomment_metadata_conv);
+
+ return updates;
+}