PR #24741 opened by Romain Beauxis (toots) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24741 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24741.patch
This PR makes sure chained ogg streams created using `bitexact` have different serials on each link. >From eaa188079af070bf619ab79763b27544ad9c7f45 Mon Sep 17 00:00:00 2001 From: Romain Beauxis <[email protected]> Date: Sun, 27 Sep 2026 12:07:26 -0500 Subject: [PATCH] avformat/oggenc: derive bitexact serial numbers from the stream headers --- doc/muxers.texi | 7 ++-- libavformat/oggenc.c | 50 ++++++++++++++++++------ tests/ref/fate/limited_input_seek | 2 +- tests/ref/fate/limited_input_seek-copyts | 2 +- tests/ref/lavf-fate/vp3.ogg | 2 +- tests/ref/lavf-fate/vp8.ogg | 2 +- tests/ref/lavf/ogg | 2 +- 7 files changed, 46 insertions(+), 21 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 95f7ea33a7..4653f6a3fd 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -3414,9 +3414,10 @@ possible. A value of 1 will effectively use 1 packet-per-page in most situations, giving a small seek granularity at the cost of additional container overhead. @item -serial_offset @var{value} -Serial value from which to set the streams serial number. -Setting it to different and sufficiently large values ensures that the produced -ogg files can be safely chained. +Offset for the stream serial numbers in bitexact mode. +Each serial number is then derived from the offset and the stream's headers. +Chained links need distinct serial numbers. +Links with identical headers and tags only get them from different offsets. @end table diff --git a/libavformat/oggenc.c b/libavformat/oggenc.c index 5c7e3da0bc..691ffd919d 100644 --- a/libavformat/oggenc.c +++ b/libavformat/oggenc.c @@ -486,17 +486,43 @@ static void ogg_write_pages(AVFormatContext *s, int flush) ogg->page_list = p; } +/** + * Derive a bitexact serial number from the stream's header packets, so that + * links muxed apart with different headers or tags still chain validly. + */ +static unsigned ogg_bitexact_serial(const OGGStreamContext *oggstream, unsigned seed) +{ + const AVCRC *table = av_crc_get_table(AV_CRC_32_IEEE); + uint32_t crc = seed; + + for (int i = 0; i < 3; i++) + if (oggstream->header_len[i]) + crc = av_crc(table, crc, oggstream->header[i], oggstream->header_len[i]); + + return crc; +} + +static int ogg_serial_in_use(AVFormatContext *s, int nb_streams, unsigned serial_num) +{ + for (int i = 0; i < nb_streams; i++) { + const OGGStreamContext *oggstream = s->streams[i]->priv_data; + if (oggstream->serial_num == serial_num) + return 1; + } + return 0; +} + // This function can be used on an initialized context to reinitialize the // streams. static int ogg_init(AVFormatContext *s) { OGGContext *ogg = s->priv_data; OGGStreamContext *oggstream = NULL; - int i, j; + int i; for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; - unsigned serial_num = i + ogg->serial_offset; + unsigned serial_num; if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { if (st->codecpar->codec_id == AV_CODEC_ID_OPUS) @@ -535,17 +561,6 @@ static int ogg_init(AVFormatContext *s) oggstream->page.stream_index = i; - if (!(s->flags & AVFMT_FLAG_BITEXACT)) - do { - serial_num = av_get_random_seed(); - for (j = 0; j < i; j++) { - OGGStreamContext *sc = s->streams[j]->priv_data; - if (serial_num == sc->serial_num) - break; - } - } while (j < i); - oggstream->serial_num = serial_num; - av_dict_copy(&st->metadata, s->metadata, AV_DICT_DONT_OVERWRITE); if (st->codecpar->codec_id == AV_CODEC_ID_FLAC) { @@ -620,6 +635,15 @@ static int ogg_init(AVFormatContext *s) oggstream->kfgshift, oggstream->vrev); } } + + if (s->flags & AVFMT_FLAG_BITEXACT) + serial_num = ogg_bitexact_serial(oggstream, i + ogg->serial_offset); + else + serial_num = av_get_random_seed(); + while (ogg_serial_in_use(s, i, serial_num)) + serial_num = s->flags & AVFMT_FLAG_BITEXACT ? serial_num + 1 + : av_get_random_seed(); + oggstream->serial_num = serial_num; } return 0; diff --git a/tests/ref/fate/limited_input_seek b/tests/ref/fate/limited_input_seek index 7a5715cf17..95f50a107f 100644 --- a/tests/ref/fate/limited_input_seek +++ b/tests/ref/fate/limited_input_seek @@ -1 +1 @@ -4498ed5370e5a15904bad5d5a716d0b9 +e1068afb583b5f21a6faff94b458fd8f diff --git a/tests/ref/fate/limited_input_seek-copyts b/tests/ref/fate/limited_input_seek-copyts index 527a1fb0ad..f9b47b070e 100644 --- a/tests/ref/fate/limited_input_seek-copyts +++ b/tests/ref/fate/limited_input_seek-copyts @@ -1 +1 @@ -7a0febe8623a2838ac5878f58159c74f +63e0dd3f351d2fb43f0cdfd12e517496 diff --git a/tests/ref/lavf-fate/vp3.ogg b/tests/ref/lavf-fate/vp3.ogg index f4e22dc16b..28491cec4e 100644 --- a/tests/ref/lavf-fate/vp3.ogg +++ b/tests/ref/lavf-fate/vp3.ogg @@ -1,3 +1,3 @@ -4bd51dac3194fa88ae33767c25b4b1e6 *tests/data/lavf-fate/lavf.vp3.ogg +f89ded5881b4b002905594c3ff90f8ec *tests/data/lavf-fate/lavf.vp3.ogg 417621 tests/data/lavf-fate/lavf.vp3.ogg tests/data/lavf-fate/lavf.vp3.ogg CRC=0x037e3e79 diff --git a/tests/ref/lavf-fate/vp8.ogg b/tests/ref/lavf-fate/vp8.ogg index dd4ed162f9..8a5ef0909a 100644 --- a/tests/ref/lavf-fate/vp8.ogg +++ b/tests/ref/lavf-fate/vp8.ogg @@ -1,3 +1,3 @@ -20f1e9b1714513a0ba85ca636e818784 *tests/data/lavf-fate/lavf.vp8.ogg +c396feea7c83ccc361b94af00967da2a *tests/data/lavf-fate/lavf.vp8.ogg 95009 tests/data/lavf-fate/lavf.vp8.ogg tests/data/lavf-fate/lavf.vp8.ogg CRC=0xfb8d7a66 diff --git a/tests/ref/lavf/ogg b/tests/ref/lavf/ogg index 3ac10e6f7c..0e7fa29933 100644 --- a/tests/ref/lavf/ogg +++ b/tests/ref/lavf/ogg @@ -1,3 +1,3 @@ -81b9366cacb23644c2803585dced9996 *tests/data/lavf/lavf.ogg +f3e4bad31e324f91ffe525106dd53148 *tests/data/lavf/lavf.ogg 13516 tests/data/lavf/lavf.ogg tests/data/lavf/lavf.ogg CRC=0x3a1da17e -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
