mirror of
https://git.ffmpeg.org/ffmpeg.git
synced 2026-10-24 02:09:03 +03:00
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 <jamrial@gmail.com>
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
|
||||
@@ -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 &&
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user