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 */