PR #25662 opened by superuser404 URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25662 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25662.patch
Attached files are read in full in `matroska_read_header()`, whether the Attachments element precedes the first Cluster (where mkvmerge and lavf write it) or follows the Clusters and is reached through the SeekHead. In practice they are the fonts of ASS subtitles, and releases carrying tens of megabytes of them are common. A caller that does not use them still waits for the whole transfer before the first packet; over a network input that dominates the open (the case that prompted this: 23 MB of fonts, about 11 s over a WAN connection). This adds a `skip_attachments` demuxer option (default 0). When set: - an Attachments element of known length is skipped like an element without a syntax, - the SeekHead entry pointing at it is not followed, - no attachment streams are exported, and tags targeting an attachment are dropped without the "non-existent attachment" warning, since their target was skipped on purpose. Tracks are created before attachment streams, so stream indices and packets are unchanged. An Attachments element of unknown length is still parsed as before. **Measurements** (ffprobe, `-v debug` AVIOContext statistics), 26 MB files with 24 MB of fonts: | layout | without | with `-skip_attachments 1` | |---|---|---| | attachments before the first Cluster (mkvmerge) | 25.15 MB read | 0.16 MB | | attachments after the Clusters, via SeekHead (mkvpropedit) | 25.18 MB, 4 seeks | 0.16 MB, 2 seeks | Packet lists (`-show_entries packet=pts_time,stream_index`) and a seek to 6 s are identical with and without the option in both layouts. **Tests:** new `fate-matroska-skip-attachments` on the existing `mkv/hdr10tags-both.mkv`, whose attachment follows the first Cluster, so the SeekHead path is covered; no new sample needed. `make fate-matroska` (55 tests) and `fate-source` pass. `doc/demuxers.texi` gains a matroska section for the option (I could not run makeinfo locally; the markup follows the neighbouring sections). lavf micro bumped. A possible alternative would be reading attachments lazily on demand, but attachment data is exported as codecpar extradata at header time, so that would change the API contract; an opt-in option keeps the current behaviour untouched for everyone who does not set it. >From 8adca69ad395bc26e981556fb6c6a28548d5860d Mon Sep 17 00:00:00 2001 From: Vincent Herbst <[email protected]> Date: Sun, 1 Nov 2026 20:13:37 +0100 Subject: [PATCH] avformat/matroskadec: add skip_attachments option Attached files are read in full in matroska_read_header(), whether the Attachments element precedes the first Cluster (where mkvmerge and lavf write it) or follows the Clusters and is reached through the SeekHead. In practice they are the fonts of ASS subtitles, and releases that carry tens of megabytes of them are common. A caller that does not use them still waits for that whole transfer before the first packet, which over a network input dominates the time to open (a reported case: 23 MB of fonts, about 11 s). The new skip_attachments option (default 0) skips the element instead: inline it is passed over like an element without a syntax, and the SeekHead entry pointing at it is not followed. No attachment streams are exported, and tags targeting an attachment are dropped without the "non-existent attachment" warning, since their target was skipped on purpose. Tracks are created before attachment streams, so stream indices and packets are unchanged. Measured with ffprobe on 26 MB files carrying 24 MB of fonts, in both layouts: about 25.2 MB read to open without the option, 0.13 to 0.16 MB with it, identical packets and seeks either way. The FATE test uses mkv/hdr10tags-both.mkv, whose attachment follows the first Cluster, so it covers the SeekHead path. Signed-off-by: Vincent Herbst <[email protected]> --- doc/demuxers.texi | 17 +++++++++++++++++ libavformat/matroskadec.c | 24 +++++++++++++++++++++++- libavformat/version.h | 2 +- tests/fate/matroska.mak | 5 +++++ tests/ref/fate/matroska-skip-attachments | 1 + 5 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 tests/ref/fate/matroska-skip-attachments diff --git a/doc/demuxers.texi b/doc/demuxers.texi index bc8116d9d23..c7dc7a24444 100644 --- a/doc/demuxers.texi +++ b/doc/demuxers.texi @@ -889,6 +889,23 @@ Set the sample rate for libopenmpt to output. Range is from 1000 to INT_MAX. The value default is 48000. @end table +@section matroska + +Matroska and WebM demuxer. + +It accepts the following options: + +@table @option +@item skip_attachments +Skip the attached files (Attachments element) instead of reading them while +opening the file, both where they precede the first Cluster and where only the +SeekHead refers to them. No attachment streams are exported, and tags that +target an attachment are dropped. Attachments are commonly the fonts of ASS +subtitles and can be tens of megabytes, all of which are otherwise read before +the first packet; this is useful for callers that do not use them, especially +over slow inputs. Default is 0. +@end table + @anchor{mccdec} @section mcc diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c index d36cc5c3452..e78476142b4 100644 --- a/libavformat/matroskadec.c +++ b/libavformat/matroskadec.c @@ -439,6 +439,9 @@ typedef struct MatroskaDemuxContext { /* Bandwidth value for WebM DASH Manifest */ int bandwidth; + + /* Skip the Attachments element instead of reading it */ + int skip_attachments; } MatroskaDemuxContext; #define CHILD_OF(parent) { .def = { .n = parent } } @@ -1476,6 +1479,9 @@ static int ebml_parse(MatroskaDemuxContext *matroska, if (!data && length != EBML_UNKNOWN_LENGTH) goto skip; + if (id == MATROSKA_ID_ATTACHMENTS && matroska->skip_attachments && + length != EBML_UNKNOWN_LENGTH) + goto skip; } switch (syntax->type) { @@ -1872,7 +1878,7 @@ static void matroska_convert_tags(AVFormatContext *s) found = 1; } } - if (!found) { + if (!found && !matroska->skip_attachments) { av_log(s, AV_LOG_WARNING, "The tags at index %d refer to a " "non-existent attachment %"PRId64".\n", @@ -1985,6 +1991,9 @@ static void matroska_execute_seekhead(MatroskaDemuxContext *matroska) if (id == MATROSKA_ID_CUES) continue; + if (id == MATROSKA_ID_ATTACHMENTS && matroska->skip_attachments) + continue; + if (matroska_parse_seekhead_entry(matroska, pos) < 0) { // mark index as broken matroska->cues_parsing_deferred = -1; @@ -5052,11 +5061,24 @@ const FFInputFormat ff_webm_dash_manifest_demuxer = { }; #endif +static const AVOption matroska_options[] = { + { "skip_attachments", "skip the attached files instead of reading them while opening", offsetof(MatroskaDemuxContext, skip_attachments), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +static const AVClass matroska_class = { + .class_name = "matroska,webm demuxer", + .item_name = av_default_item_name, + .option = matroska_options, + .version = LIBAVUTIL_VERSION_INT, +}; + const FFInputFormat ff_matroska_demuxer = { .p.name = "matroska,webm", .p.long_name = NULL_IF_CONFIG_SMALL("Matroska / WebM"), .p.extensions = "mkv,mk3d,mka,mks,webm", .p.mime_type = "audio/webm,audio/x-matroska,video/webm,video/x-matroska", + .p.priv_class = &matroska_class, .priv_data_size = sizeof(MatroskaDemuxContext), .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = matroska_probe, diff --git a/libavformat/version.h b/libavformat/version.h index 6a80f3ac4e6..be8ce011609 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -32,7 +32,7 @@ #include "version_major.h" #define LIBAVFORMAT_VERSION_MINOR 18 -#define LIBAVFORMAT_VERSION_MICRO 100 +#define LIBAVFORMAT_VERSION_MICRO 101 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ LIBAVFORMAT_VERSION_MINOR, \ diff --git a/tests/fate/matroska.mak b/tests/fate/matroska.mak index f165ce19476..250c34ff4b9 100644 --- a/tests/fate/matroska.mak +++ b/tests/fate/matroska.mak @@ -249,6 +249,11 @@ fate-matroska-dvbsub-remux: CMD = transcode mpegts $(TARGET_SAMPLES)/sub/dvbsubt FATE_MATROSKA_FFPROBE-$(call DEMDEC, MATROSKA, H264) += fate-matroska-spherical-mono fate-matroska-spherical-mono: CMD = run ffprobe$(PROGSSUF)$(EXESUF) -show_entries stream_side_data_list -select_streams v -v 0 $(TARGET_SAMPLES)/mkv/spherical.mkv +# The attachment of this file follows the first Cluster and is only reachable +# through the SeekHead; skip_attachments must not export it. +FATE_MATROSKA_FFPROBE-$(call ALLYES, MATROSKA_DEMUXER) += fate-matroska-skip-attachments +fate-matroska-skip-attachments: CMD = run ffprobe$(PROGSSUF)$(EXESUF) -show_entries stream=index,codec_type -of csv=p=0 -v 0 -skip_attachments 1 $(TARGET_SAMPLES)/mkv/hdr10tags-both.mkv + # This test tests the handling of AVStereo3D information, in particular # the ability to set it via metadata in the muxer (the file itself is # actually an ordinary file with a single view). It also tests diff --git a/tests/ref/fate/matroska-skip-attachments b/tests/ref/fate/matroska-skip-attachments new file mode 100644 index 00000000000..47bd876f767 --- /dev/null +++ b/tests/ref/fate/matroska-skip-attachments @@ -0,0 +1 @@ +0,video -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
