This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit 8b577f90f3676cd5540e135e974dbdb1462ec4bf Author: James Almer <[email protected]> AuthorDate: Mon Oct 5 00:11:37 2026 -0300 Commit: James Almer <[email protected]> CommitDate: Mon Oct 12 10:29:07 2026 -0300 avformat: allow Stream Group to reference other Stream Groups Some formats, like ISOBMFF, allow having references between items that we export as Stream Groups where the entries are other items that we too export as Stream Groups. This is implemented in a backwards compatible way, where if a group were to contain an entry that points to another group, the legacy nb_streams field will be set to 0 so callers not aware of the new feature don't attempt to parse the group at all. Signed-off-by: James Almer <[email protected]> --- libavformat/avformat.c | 19 +++++++++--- libavformat/avformat.h | 76 +++++++++++++++++++++++++++++++++++++++++++-- libavformat/options.c | 76 +++++++++++++++++++++++++++++++++++++++++++-- libavformat/version_major.h | 2 ++ 4 files changed, 163 insertions(+), 10 deletions(-) diff --git a/libavformat/avformat.c b/libavformat/avformat.c index d0cda8f3b4..f0009eaae5 100644 --- a/libavformat/avformat.c +++ b/libavformat/avformat.c @@ -84,7 +84,14 @@ void ff_free_stream_group(AVStreamGroup **pstg) if (!stg) return; +#if FF_API_STREAM_GROUP_ENTRIES +FF_DISABLE_DEPRECATION_WARNINGS av_freep(&stg->streams); +FF_ENABLE_DEPRECATION_WARNINGS +#endif + for (int i = 0; i < stg->nb_entries; i++) + av_freep(&stg->entries[i]); + av_freep(&stg->entries); av_dict_free(&stg->metadata); av_freep(&stg->priv_data); switch (stg->type) { @@ -642,8 +649,10 @@ static int match_stream_specifier(const AVFormatContext *s, const AVStream *st, } if (group_idx < 0 || group_idx >= s->nb_stream_groups) return AVERROR(EINVAL); - for (unsigned j = 0; j < s->stream_groups[group_idx]->nb_streams; j++) { - if (st->index == s->stream_groups[group_idx]->streams[j]->index) { + for (unsigned j = 0; j < s->stream_groups[group_idx]->nb_entries; j++) { + if (s->stream_groups[group_idx]->entries[j]->type != AV_STREAM_GROUP_ENTRY_STREAM) + continue; + if (st->index == s->stream_groups[group_idx]->entries[j]->stream->index) { found = 1; if (g) *g = s->stream_groups[group_idx]; @@ -772,9 +781,11 @@ int avformat_match_stream_specifier(AVFormatContext *s, AVStream *st, return (index == st->index); /* If we requested a matching stream index, we have to ensure st is that. */ - nb_streams = g ? g->nb_streams : (p ? p->nb_stream_indexes : s->nb_streams); + nb_streams = g ? g->nb_entries : (p ? p->nb_stream_indexes : s->nb_streams); for (int i = 0; i < nb_streams && index >= 0; i++) { - unsigned idx = g ? g->streams[i]->index : (p ? p->stream_index[i] : i); + if (g && g->entries[i]->type != AV_STREAM_GROUP_ENTRY_STREAM) + continue; + unsigned idx = g ? g->entries[i]->stream->index : (p ? p->stream_index[i] : i); const AVStream *candidate = s->streams[idx]; ret = match_stream_specifier(s, candidate, spec, NULL, NULL, NULL); if (ret < 0) diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 07ed2764cd..6279150609 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -1162,6 +1162,19 @@ enum AVStreamGroupParamsType { struct AVIAMFAudioElement; struct AVIAMFMixPresentation; +enum AVStreamGroupEntryType { + AV_STREAM_GROUP_ENTRY_STREAM, + AV_STREAM_GROUP_ENTRY_STREAM_GROUP, +}; + +typedef struct AVStreamGroupEntry { + enum AVStreamGroupEntryType type; + union { + AVStream *stream; + struct AVStreamGroup *stream_group; + }; +} AVStreamGroupEntry; + typedef struct AVStreamGroup { /** * A class for @ref avoptions. Set by avformat_stream_group_create(). @@ -1219,11 +1232,15 @@ typedef struct AVStreamGroup { */ AVDictionary *metadata; +#if FF_API_STREAM_GROUP_ENTRIES /** * Number of elements in AVStreamGroup.streams. * - * Set by avformat_stream_group_add_stream() must not be modified by any other code. + * Set by avformat_stream_group_add_stream(), must not be modified by any other code. + * + * @deprecated Use AVStreamGroup.nb_entries. */ + attribute_deprecated unsigned int nb_streams; /** @@ -1236,8 +1253,12 @@ typedef struct AVStreamGroup { * - muxing: entries are created by the user before avformat_write_header(). * * Freed by libavformat in avformat_free_context(). + * + * @deprecated Use AVStreamGroup.entries. */ + attribute_deprecated AVStream **streams; +#endif /** * Stream group disposition - a combination of AV_DISPOSITION_* flags. @@ -1248,6 +1269,27 @@ typedef struct AVStreamGroup { * - muxing: may be set by the caller before avformat_write_header(). */ int disposition; + + /** + * Number of elements in AVStreamGroup.entries. + * + * Set by avformat_stream_group_add_stream() or avformat_stream_group_add_stream_group(), + * must not be modified by any other code. + */ + unsigned int nb_entries; + + /** + * A list of streams or child groups in the group. New entries are created with + * or avformat_stream_group_add_stream() or avformat_stream_group_add_stream_group(). + * + * - demuxing: entries are created by libavformat on group creation. + * If AVFMTCTX_NOHEADER is set in ctx_flags, then new entries may also + * appear in av_read_frame(). + * - muxing: entries are created by the user before avformat_write_header(). + * + * Freed by libavformat in avformat_free_context(). + */ + AVStreamGroupEntry **entries; } AVStreamGroup; struct AVCodecParserContext *av_stream_get_parser(const AVStream *s); @@ -1414,7 +1456,8 @@ typedef struct AVFormatContext { unsigned int nb_stream_groups; /** * A list of all stream groups in the file. New groups are created with - * avformat_stream_group_create(), and filled with avformat_stream_group_add_stream(). + * avformat_stream_group_create(), and filled with avformat_stream_group_add_stream() + * and avformat_stream_group_add_stream_group. * * - demuxing: groups may be created by libavformat in avformat_open_input(). * If AVFMTCTX_NOHEADER is set in ctx_flags, then new groups may also @@ -2116,11 +2159,13 @@ const char *avformat_stream_group_name(enum AVStreamGroupParamsType type); * by avformat_stream_group_create(). * * New streams can be added to the group with avformat_stream_group_add_stream(). + * Child groups can be added to the group with avformat_stream_group_add_stream_group(). * * @param s media file handle * * @return newly created group or NULL on error. - * @see avformat_new_stream, avformat_stream_group_add_stream. + * @see avformat_new_stream. + * @see avformat_stream_group_add_stream, avformat_stream_group_add_stream_group. */ AVStreamGroup *avformat_stream_group_create(AVFormatContext *s, enum AVStreamGroupParamsType type, @@ -2170,6 +2215,31 @@ AVStream *avformat_new_stream(AVFormatContext *s, const struct AVCodec *c); */ int avformat_stream_group_add_stream(AVStreamGroup *stg, AVStream *st); +/** + * Add an already allocated stream group to another stream group. + * + * When demuxing, it may be called by the demuxer in read_header(). If the + * flag AVFMTCTX_NOHEADER is set in s.ctx_flags, then it may also + * be called in read_packet(). + * + * When muxing, may be called by the user before avformat_write_header() after + * having allocated a both the parent and child groups with + * avformat_stream_group_create(). + * + * User is required to call avformat_free_context() to clean up the allocation + * by avformat_stream_group_add_stream_group(). + * + * @param stg stream group belonging to a media file. + * @param group stream group in the media file to add to the group. + * + * @retval 0 success + * @retval AVERROR(EEXIST) the stream group was already in the group + * @retval "another negative error code" legitimate errors + * + * @see avformat_stream_group_create. + */ +int avformat_stream_group_add_stream_group(AVStreamGroup *stg, AVStreamGroup *group); + AVProgram *av_new_program(AVFormatContext *s, int id); diff --git a/libavformat/options.c b/libavformat/options.c index 042fe494cf..e1c094f538 100644 --- a/libavformat/options.c +++ b/libavformat/options.c @@ -547,13 +547,37 @@ fail: static int stream_group_add_stream(AVStreamGroup *stg, AVStream *st) { +#if FF_API_STREAM_GROUP_ENTRIES +FF_DISABLE_DEPRECATION_WARNINGS AVStream **streams = av_realloc_array(stg->streams, stg->nb_streams + 1, sizeof(*stg->streams)); if (!streams) return AVERROR(ENOMEM); +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + AVStreamGroupEntry **entries = av_realloc_array(stg->entries, stg->nb_entries + 1, + sizeof(*stg->entries)); + if (!entries) + return AVERROR(ENOMEM); + stg->entries = entries; + stg->entries[stg->nb_entries] = av_mallocz(sizeof(*stg->entries[stg->nb_entries])); + if (!stg->entries[stg->nb_entries]) + return AVERROR(ENOMEM); + + stg->entries[stg->nb_entries]->type = AV_STREAM_GROUP_ENTRY_STREAM; + stg->entries[stg->nb_entries++]->stream = st; + +#if FF_API_STREAM_GROUP_ENTRIES +FF_DISABLE_DEPRECATION_WARNINGS stg->streams = streams; - stg->streams[stg->nb_streams++] = st; + if (stg->nb_streams == (stg->nb_entries - 1)) + stg->streams[stg->nb_streams++] = st; + else + stg->nb_streams = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif return 0; } @@ -566,13 +590,59 @@ int avformat_stream_group_add_stream(AVStreamGroup *stg, AVStream *st) if (stgi->fmtctx != sti->fmtctx) return AVERROR(EINVAL); - for (int i = 0; i < stg->nb_streams; i++) - if (stg->streams[i]->index == st->index) + for (int i = 0; i < stg->nb_entries; i++) { + const AVStreamGroupEntry *entry = stg->entries[i]; + if (entry->type != AV_STREAM_GROUP_ENTRY_STREAM) + continue; + if (entry->stream->index == st->index) return AVERROR(EEXIST); + } return stream_group_add_stream(stg, st); } +static int stream_group_add_stream_group(AVStreamGroup *stg, AVStreamGroup *group) +{ + AVStreamGroupEntry **entries = av_realloc_array(stg->entries, stg->nb_entries + 1, + sizeof(*stg->entries)); + if (!entries) + return AVERROR(ENOMEM); + + stg->entries = entries; + stg->entries[stg->nb_entries] = av_mallocz(sizeof(*stg->entries[stg->nb_entries])); + if (!stg->entries[stg->nb_entries]) + return AVERROR(ENOMEM); + + stg->entries[stg->nb_entries]->type = AV_STREAM_GROUP_ENTRY_STREAM_GROUP; + stg->entries[stg->nb_entries++]->stream_group = group; +#if FF_API_STREAM_GROUP_ENTRIES +FF_DISABLE_DEPRECATION_WARNINGS + stg->nb_streams = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + return 0; +} + +int avformat_stream_group_add_stream_group(AVStreamGroup *stg, AVStreamGroup *group) +{ + const FFStreamGroup *stgi = cffstreamgroup(stg); + const FFStreamGroup *groupi = cffstreamgroup(group); + + if (stgi->fmtctx != groupi->fmtctx) + return AVERROR(EINVAL); + + for (int i = 0; i < stg->nb_entries; i++) { + const AVStreamGroupEntry *entry = stg->entries[i]; + if (entry->type != AV_STREAM_GROUP_ENTRY_STREAM_GROUP) + continue; + if (entry->stream_group->index == group->index) + return AVERROR(EEXIST); + } + + return stream_group_add_stream_group(stg, group); +} + static int option_is_disposition(const AVOption *opt) { return opt->type == AV_OPT_TYPE_CONST && diff --git a/libavformat/version_major.h b/libavformat/version_major.h index f0eaa170b4..9a7955d9e4 100644 --- a/libavformat/version_major.h +++ b/libavformat/version_major.h @@ -49,6 +49,8 @@ #define FF_API_OLD_ID3V2_COMMENT (LIBAVFORMAT_VERSION_MAJOR < 64) +#define FF_API_STREAM_GROUP_ENTRIES (LIBAVFORMAT_VERSION_MAJOR < 64) + #define FF_API_R_FRAME_RATE 1 #endif /* AVFORMAT_VERSION_MAJOR_H */ -- To stop receiving notification emails like this one, please contact [email protected]. _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
