This is an automated email from the git hooks/post-receive script.
Git pushed a commit to branch master
in repository ffmpeg.
The following commit(s) were added to refs/heads/master by this push:
new 0e89d993c5 libavformat/oggenc.c: re-initialize stream on new metadata.
0e89d993c5 is described below
commit 0e89d993c5bc6c7ed4d9a05f750bd5a7130836ea
Author: Romain Beauxis <[email protected]>
AuthorDate: Fri Mar 14 00:27:04 2025 -0500
Commit: Lynne <[email protected]>
CommitDate: Wed Feb 18 16:25:06 2026 +0000
libavformat/oggenc.c: re-initialize stream on new metadata.
---
libavformat/oggenc.c | 104 +++++++++++++++++++++++++++++++++++++++-------
tests/fate-run.sh | 10 +++++
tests/fate/ogg-flac.mak | 5 +++
tests/fate/ogg-opus.mak | 5 +++
tests/fate/ogg-vorbis.mak | 5 +++
5 files changed, 115 insertions(+), 14 deletions(-)
diff --git a/libavformat/oggenc.c b/libavformat/oggenc.c
index be85386a6a..605c9d20e1 100644
--- a/libavformat/oggenc.c
+++ b/libavformat/oggenc.c
@@ -66,6 +66,7 @@ typedef struct OGGStreamContext {
OGGPage page; ///< current page
unsigned serial_num; ///< serial number
int64_t last_granule; ///< last packet granule
+ int packet_seen; ///< true when packets have been submitted
} OGGStreamContext;
typedef struct OGGPageList {
@@ -81,11 +82,14 @@ typedef struct OGGContext {
#endif
int64_t pref_duration; ///< preferred page duration (0 => fill all
segments)
int serial_offset;
+ int failed; // if true all packet submission will fail.
} OGGContext;
#define OFFSET(x) offsetof(OGGContext, x)
#define PARAM AV_OPT_FLAG_ENCODING_PARAM
+static int ogg_write_trailer(AVFormatContext *s);
+
static const AVOption options[] = {
{ "serial_offset", "serial number offset",
OFFSET(serial_offset), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX,
PARAM },
@@ -107,6 +111,20 @@ static const AVClass ogg_muxer_class = {
.version = LIBAVUTIL_VERSION_INT,
};
+static void ogg_cleanup_stream(AVStream *st)
+{
+ OGGStreamContext *oggstream = st->priv_data;
+
+ if (st->codecpar->codec_id == AV_CODEC_ID_FLAC ||
+ st->codecpar->codec_id == AV_CODEC_ID_SPEEX ||
+ st->codecpar->codec_id == AV_CODEC_ID_OPUS ||
+ st->codecpar->codec_id == AV_CODEC_ID_VP8) {
+ av_freep(&oggstream->header[0]);
+ }
+
+ av_freep(&oggstream->header[1]);
+}
+
static void ogg_write_page(AVFormatContext *s, OGGPage *page, int extra_flags)
{
OGGStreamContext *oggstream = s->streams[page->stream_index]->priv_data;
@@ -482,6 +500,8 @@ static void ogg_write_pages(AVFormatContext *s, int flush)
ogg->page_list = p;
}
+// This function can be used on an initialized context to reinitialize the
+// streams.
static int ogg_init(AVFormatContext *s)
{
OGGContext *ogg = s->priv_data;
@@ -515,9 +535,17 @@ static int ogg_init(AVFormatContext *s)
av_log(s, AV_LOG_ERROR, "No extradata present\n");
return AVERROR_INVALIDDATA;
}
- oggstream = av_mallocz(sizeof(*oggstream));
- if (!oggstream)
- return AVERROR(ENOMEM);
+ oggstream = st->priv_data;
+
+ if (!oggstream) {
+ oggstream = av_mallocz(sizeof(*oggstream));
+ if (!oggstream)
+ return AVERROR(ENOMEM);
+ st->priv_data = oggstream;
+ } else {
+ ogg_cleanup_stream(st);
+ memset(oggstream, 0, sizeof(*oggstream));
+ }
oggstream->page.stream_index = i;
@@ -534,7 +562,6 @@ static int ogg_init(AVFormatContext *s)
av_dict_copy(&st->metadata, s->metadata, AV_DICT_DONT_OVERWRITE);
- st->priv_data = oggstream;
if (st->codecpar->codec_id == AV_CODEC_ID_FLAC) {
int err = ogg_build_flac_headers(st->codecpar, oggstream,
s->flags & AVFMT_FLAG_BITEXACT,
@@ -642,13 +669,69 @@ static int ogg_write_header(AVFormatContext *s)
return 0;
}
+static int ogg_check_new_metadata(AVFormatContext *s, AVPacket *pkt)
+{
+ int ret = 0;
+ size_t size;
+ OGGContext *oggcontext = s->priv_data;
+ AVStream *st = s->streams[pkt->stream_index];
+ OGGStreamContext *oggstream = st->priv_data;
+ const uint8_t *side_metadata = av_packet_get_side_data(pkt,
AV_PKT_DATA_STRINGS_METADATA, &size);
+
+ if (!side_metadata)
+ return 0;
+
+ // Don't restart on first packet.
+ if (!oggstream->packet_seen)
+ return 0;
+
+ if (s->nb_streams > 1) {
+ av_log(s, AV_LOG_WARNING, "Multiple streams present: cannot insert new
metadata!\n");
+ return 0;
+ }
+
+ if (st->codecpar->codec_id != AV_CODEC_ID_VORBIS &&
+ st->codecpar->codec_id != AV_CODEC_ID_FLAC &&
+ st->codecpar->codec_id != AV_CODEC_ID_OPUS) {
+ av_log(s, AV_LOG_WARNING, "Inserting metadata is only supported for
vorbis, flac and opus streams!\n");
+ return 0;
+ }
+
+ ret = ogg_write_trailer(s);
+ if (ret < 0)
+ goto end;
+
+ av_dict_free(&st->metadata);
+ ret = av_packet_unpack_dictionary(side_metadata, size, &st->metadata);
+ if (ret < 0)
+ goto end;
+
+ ret = ogg_init(s);
+ if (ret < 0)
+ goto end;
+
+ ret = ogg_write_header(s);
+
+end:
+ oggcontext->failed = ret < 0;
+ return ret;
+}
+
static int ogg_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
{
AVStream *st = s->streams[pkt->stream_index];
+ OGGContext *oggcontext = s->priv_data;
OGGStreamContext *oggstream = st->priv_data;
int ret;
int64_t granule;
+ if (oggcontext->failed)
+ return AVERROR_INVALIDDATA;
+
+ ret = ogg_check_new_metadata(s, pkt);
+ if (ret < 0)
+ return ret;
+
if (st->codecpar->codec_id == AV_CODEC_ID_THEORA) {
int64_t pts = oggstream->vrev < 1 ? pkt->pts : pkt->pts +
pkt->duration;
int pframe_count;
@@ -690,6 +773,7 @@ static int ogg_write_packet_internal(AVFormatContext *s,
AVPacket *pkt)
ogg_write_pages(s, 0);
oggstream->last_granule = granule;
+ oggstream->packet_seen = 1;
return 0;
}
@@ -736,16 +820,8 @@ static void ogg_free(AVFormatContext *s)
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
- OGGStreamContext *oggstream = st->priv_data;
- if (!oggstream)
- continue;
- if (st->codecpar->codec_id == AV_CODEC_ID_FLAC ||
- st->codecpar->codec_id == AV_CODEC_ID_SPEEX ||
- st->codecpar->codec_id == AV_CODEC_ID_OPUS ||
- st->codecpar->codec_id == AV_CODEC_ID_VP8) {
- av_freep(&oggstream->header[0]);
- }
- av_freep(&oggstream->header[1]);
+ if (st->priv_data)
+ ogg_cleanup_stream(st);
}
while (p) {
diff --git a/tests/fate-run.sh b/tests/fate-run.sh
index 0e0c11ac3b..cd66cd059c 100755
--- a/tests/fate-run.sh
+++ b/tests/fate-run.sh
@@ -103,6 +103,16 @@ runecho(){
$target_exec $target_path/"$@" >&3
}
+run_with_temp(){
+ create_tmp=$1
+ process_tmp=$2
+ filext=$3
+ tmpfile=${outdir}/$test.$filext
+ cleanfiles="$cleanfiles $tmpfile"
+ run $create_tmp $tmpfile || return 1
+ run $process_tmp $tmpfile
+}
+
probefmt(){
run ffprobe${PROGSUF}${EXECSUF} -bitexact -threads $threads -show_entries
format=format_name -print_format default=nw=1:nk=1 "$@"
}
diff --git a/tests/fate/ogg-flac.mak b/tests/fate/ogg-flac.mak
index 07bdb232cb..3b48f32d2d 100644
--- a/tests/fate/ogg-flac.mak
+++ b/tests/fate/ogg-flac.mak
@@ -2,6 +2,11 @@ FATE_OGG_FLAC += fate-ogg-flac-chained-meta
fate-ogg-flac-chained-meta: REF =
$(SRC_PATH)/tests/ref/fate/ogg-flac-chained-meta.txt
fate-ogg-flac-chained-meta: CMD = run
$(APITESTSDIR)/api-dump-stream-meta-test$(EXESUF)
$(TARGET_SAMPLES)/ogg-flac/chained-meta.ogg
+FATE_OGG_FLAC += fate-ogg-flac-copy-chained-meta
+fate-ogg-flac-copy-chained-meta:
$(APITESTSDIR)/api-dump-stream-meta-test$(EXESUF) $(FFMPEG)
+fate-ogg-flac-copy-chained-meta: REF =
$(SRC_PATH)/tests/ref/fate/ogg-flac-chained-meta.txt
+fate-ogg-flac-copy-chained-meta: CMD = run_with_temp "$(FFMPEG) -nostdin
-hide_banner -loglevel quiet -i $(TARGET_SAMPLES)/ogg-flac/chained-meta.ogg -c
copy -f ogg -y" "$(APITESTSDIR)/api-dump-stream-meta-test$(EXESUF)" ogg
+
FATE_OGG_FLAC-$(call DEMDEC, OGG, FLAC, FLAC_PARSER) += $(FATE_OGG_FLAC)
FATE_SAMPLES_DUMP_STREAM_META += $(FATE_OGG_FLAC-yes)
diff --git a/tests/fate/ogg-opus.mak b/tests/fate/ogg-opus.mak
index 54b6fbabde..d8a30dd058 100644
--- a/tests/fate/ogg-opus.mak
+++ b/tests/fate/ogg-opus.mak
@@ -2,6 +2,11 @@ FATE_OGG_OPUS += fate-ogg-opus-chained-meta
fate-ogg-opus-chained-meta: REF =
$(SRC_PATH)/tests/ref/fate/ogg-opus-chained-meta.txt
fate-ogg-opus-chained-meta: CMD = run
$(APITESTSDIR)/api-dump-stream-meta-test$(EXESUF)
$(TARGET_SAMPLES)/ogg-opus/chained-meta.ogg
+FATE_OGG_OPUS += fate-ogg-opus-copy-chained-meta
+fate-ogg-opus-copy-chained-meta:
$(APITESTSDIR)/api-dump-stream-meta-test$(EXESUF) $(FFMPEG)
+fate-ogg-opus-copy-chained-meta: REF =
$(SRC_PATH)/tests/ref/fate/ogg-opus-chained-meta.txt
+fate-ogg-opus-copy-chained-meta: CMD = run_with_temp "$(FFMPEG) -nostdin
-hide_banner -loglevel quiet -i $(TARGET_SAMPLES)/ogg-opus/chained-meta.ogg -c
copy -f ogg -y" "$(APITESTSDIR)/api-dump-stream-meta-test$(EXESUF)" ogg
+
FATE_OGG_OPUS-$(call DEMDEC, OGG, OPUS) += $(FATE_OGG_OPUS)
FATE_SAMPLES_DUMP_STREAM_META += $(FATE_OGG_OPUS-yes)
diff --git a/tests/fate/ogg-vorbis.mak b/tests/fate/ogg-vorbis.mak
index 74805d591e..1bca373fea 100644
--- a/tests/fate/ogg-vorbis.mak
+++ b/tests/fate/ogg-vorbis.mak
@@ -2,6 +2,11 @@ FATE_OGG_VORBIS += fate-ogg-vorbis-chained-meta
fate-ogg-vorbis-chained-meta: REF =
$(SRC_PATH)/tests/ref/fate/ogg-vorbis-chained-meta.txt
fate-ogg-vorbis-chained-meta: CMD = run
$(APITESTSDIR)/api-dump-stream-meta-test$(EXESUF)
$(TARGET_SAMPLES)/ogg-vorbis/chained-meta.ogg
+FATE_OGG_VORBIS += fate-ogg-vorbis-copy-chained-meta
+fate-ogg-vorbis-copy-chained-meta:
$(APITESTSDIR)/api-dump-stream-meta-test$(EXESUF) $(FFMPEG)
+fate-ogg-vorbis-copy-chained-meta: REF =
$(SRC_PATH)/tests/ref/fate/ogg-vorbis-chained-meta.txt
+fate-ogg-vorbis-copy-chained-meta: CMD = run_with_temp "$(FFMPEG) -nostdin
-hide_banner -loglevel quiet -i $(TARGET_SAMPLES)/ogg-vorbis/chained-meta.ogg
-c copy -f ogg -y" "$(APITESTSDIR)/api-dump-stream-meta-test$(EXESUF)" ogg
+
FATE_OGG_VORBIS-$(call DEMDEC, OGG, VORBIS) += $(FATE_OGG_VORBIS)
FATE_SAMPLES_DUMP_STREAM_META += $(FATE_OGG_VORBIS-yes)
_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]