avformat: introduce AVStreamGroup

Signed-off-by: James Almer <jamrial@gmail.com>
This commit is contained in:
James Almer 2023-11-25 11:02:11 -03:00
parent d2af93bbef
commit 556b596d1d
8 changed files with 556 additions and 35 deletions

View File

@ -2,6 +2,15 @@ The last version increases of all libraries were on 2023-02-09
API changes, most recent first:
2023-12-18 - xxxxxxxxxxx - lavc 60.19.100 - avformat.h
Add AVStreamGroup struct.
Add AVFormatContext.stream_groups and AVFormatContext.nb_stream_groups
Add avformat_stream_group_create(), avformat_stream_group_add_stream(),
and av_stream_group_get_class().
Add enum AVStreamGroupParamsType with values AV_STREAM_GROUP_PARAMS_NONE,
AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT and
AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION.
2023-12-18 - xxxxxxxxxxx - lavu 58.35.100 - iamf.h
Add a new API to support Immersive Audio Model and Formats.

View File

@ -37,9 +37,9 @@ Matches the stream with this index. E.g. @code{-threads:1 4} would set the
thread count for the second stream to 4. If @var{stream_index} is used as an
additional stream specifier (see below), then it selects stream number
@var{stream_index} from the matching streams. Stream numbering is based on the
order of the streams as detected by libavformat except when a program ID is
also specified. In this case it is based on the ordering of the streams in the
program.
order of the streams as detected by libavformat except when a stream group
specifier or program ID is also specified. In this case it is based on the
ordering of the streams in the group or program.
@item @var{stream_type}[:@var{additional_stream_specifier}]
@var{stream_type} is one of following: 'v' or 'V' for video, 'a' for audio, 's'
for subtitle, 'd' for data, and 't' for attachments. 'v' matches all video
@ -48,6 +48,17 @@ thumbnails or cover arts. If @var{additional_stream_specifier} is used, then
it matches streams which both have this type and match the
@var{additional_stream_specifier}. Otherwise, it matches all streams of the
specified type.
@item g:@var{group_specifier}[:@var{additional_stream_specifier}]
Matches streams which are in the group with the specifier @var{group_specifier}.
if @var{additional_stream_specifier} is used, then it matches streams which both
are part of the group and match the @var{additional_stream_specifier}.
@var{group_specifier} may be one of the following:
@table @option
@item @var{group_index}
Match the stream with this group index.
@item #@var{group_id} or i:@var{group_id}
Match the stream with this group id.
@end table
@item p:@var{program_id}[:@var{additional_stream_specifier}]
Matches streams which are in the program with the id @var{program_id}. If
@var{additional_stream_specifier} is used, then it matches streams which both

View File

@ -24,6 +24,7 @@
#include "libavutil/avstring.h"
#include "libavutil/channel_layout.h"
#include "libavutil/frame.h"
#include "libavutil/iamf.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/mem.h"
#include "libavutil/opt.h"
@ -80,6 +81,32 @@ FF_ENABLE_DEPRECATION_WARNINGS
av_freep(pst);
}
void ff_free_stream_group(AVStreamGroup **pstg)
{
AVStreamGroup *stg = *pstg;
if (!stg)
return;
av_freep(&stg->streams);
av_dict_free(&stg->metadata);
av_freep(&stg->priv_data);
switch (stg->type) {
case AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT: {
av_iamf_audio_element_free(&stg->params.iamf_audio_element);
break;
}
case AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION: {
av_iamf_mix_presentation_free(&stg->params.iamf_mix_presentation);
break;
}
default:
break;
}
av_freep(pstg);
}
void ff_remove_stream(AVFormatContext *s, AVStream *st)
{
av_assert0(s->nb_streams>0);
@ -88,6 +115,14 @@ void ff_remove_stream(AVFormatContext *s, AVStream *st)
ff_free_stream(&s->streams[ --s->nb_streams ]);
}
void ff_remove_stream_group(AVFormatContext *s, AVStreamGroup *stg)
{
av_assert0(s->nb_stream_groups > 0);
av_assert0(s->stream_groups[ s->nb_stream_groups - 1 ] == stg);
ff_free_stream_group(&s->stream_groups[ --s->nb_stream_groups ]);
}
/* XXX: suppress the packet queue */
void ff_flush_packet_queue(AVFormatContext *s)
{
@ -118,6 +153,9 @@ void avformat_free_context(AVFormatContext *s)
for (unsigned i = 0; i < s->nb_streams; i++)
ff_free_stream(&s->streams[i]);
for (unsigned i = 0; i < s->nb_stream_groups; i++)
ff_free_stream_group(&s->stream_groups[i]);
s->nb_stream_groups = 0;
s->nb_streams = 0;
for (unsigned i = 0; i < s->nb_programs; i++) {
@ -139,6 +177,7 @@ void avformat_free_context(AVFormatContext *s)
av_packet_free(&si->pkt);
av_packet_free(&si->parse_pkt);
av_freep(&s->streams);
av_freep(&s->stream_groups);
ff_flush_packet_queue(s);
av_freep(&s->url);
av_free(s);
@ -464,7 +503,7 @@ int av_find_best_stream(AVFormatContext *ic, enum AVMediaType type,
*/
static int match_stream_specifier(const AVFormatContext *s, const AVStream *st,
const char *spec, const char **indexptr,
const AVProgram **p)
const AVStreamGroup **g, const AVProgram **p)
{
int match = 1; /* Stores if the specifier matches so far. */
while (*spec) {
@ -493,6 +532,46 @@ static int match_stream_specifier(const AVFormatContext *s, const AVStream *st,
match = 0;
if (nopic && (st->disposition & AV_DISPOSITION_ATTACHED_PIC))
match = 0;
} else if (*spec == 'g' && *(spec + 1) == ':') {
int64_t group_idx = -1, group_id = -1;
int found = 0;
char *endptr;
spec += 2;
if (*spec == '#' || (*spec == 'i' && *(spec + 1) == ':')) {
spec += 1 + (*spec == 'i');
group_id = strtol(spec, &endptr, 0);
if (spec == endptr || (*endptr && *endptr++ != ':'))
return AVERROR(EINVAL);
spec = endptr;
} else {
group_idx = strtol(spec, &endptr, 0);
/* Disallow empty id and make sure that if we are not at the end, then another specifier must follow. */
if (spec == endptr || (*endptr && *endptr++ != ':'))
return AVERROR(EINVAL);
spec = endptr;
}
if (match) {
if (group_id > 0) {
for (unsigned i = 0; i < s->nb_stream_groups; i++) {
if (group_id == s->stream_groups[i]->id) {
group_idx = i;
break;
}
}
}
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) {
found = 1;
if (g)
*g = s->stream_groups[group_idx];
break;
}
}
}
if (!found)
match = 0;
} else if (*spec == 'p' && *(spec + 1) == ':') {
int prog_id;
int found = 0;
@ -591,10 +670,11 @@ int avformat_match_stream_specifier(AVFormatContext *s, AVStream *st,
int ret, index;
char *endptr;
const char *indexptr = NULL;
const AVStreamGroup *g = NULL;
const AVProgram *p = NULL;
int nb_streams;
ret = match_stream_specifier(s, st, spec, &indexptr, &p);
ret = match_stream_specifier(s, st, spec, &indexptr, &g, &p);
if (ret < 0)
goto error;
@ -612,10 +692,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 = p ? p->nb_stream_indexes : s->nb_streams;
nb_streams = g ? g->nb_streams : (p ? p->nb_stream_indexes : s->nb_streams);
for (int i = 0; i < nb_streams && index >= 0; i++) {
const AVStream *candidate = s->streams[p ? p->stream_index[i] : i];
ret = match_stream_specifier(s, candidate, spec, NULL, NULL);
unsigned idx = g ? g->streams[i]->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)
goto error;
if (ret > 0 && index-- == 0 && st == candidate)

View File

@ -1018,6 +1018,83 @@ typedef struct AVStream {
int pts_wrap_bits;
} AVStream;
enum AVStreamGroupParamsType {
AV_STREAM_GROUP_PARAMS_NONE,
AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT,
AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION,
};
struct AVIAMFAudioElement;
struct AVIAMFMixPresentation;
typedef struct AVStreamGroup {
/**
* A class for @ref avoptions. Set by avformat_stream_group_create().
*/
const AVClass *av_class;
void *priv_data;
/**
* Group index in AVFormatContext.
*/
unsigned int index;
/**
* Group type-specific group ID.
*
* decoding: set by libavformat
* encoding: may set by the user
*/
int64_t id;
/**
* Group type
*
* decoding: set by libavformat on group creation
* encoding: set by avformat_stream_group_create()
*/
enum AVStreamGroupParamsType type;
/**
* Group type-specific parameters
*/
union {
struct AVIAMFAudioElement *iamf_audio_element;
struct AVIAMFMixPresentation *iamf_mix_presentation;
} params;
/**
* Metadata that applies to the whole group.
*
* - demuxing: set by libavformat on group creation
* - muxing: may be set by the caller before avformat_write_header()
*
* Freed by libavformat in avformat_free_context().
*/
AVDictionary *metadata;
/**
* Number of elements in AVStreamGroup.streams.
*
* Set by avformat_stream_group_add_stream() must not be modified by any other code.
*/
unsigned int nb_streams;
/**
* A list of streams in the group. New entries are created with
* avformat_stream_group_add_stream().
*
* - 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().
*/
AVStream **streams;
} AVStreamGroup;
struct AVCodecParserContext *av_stream_get_parser(const AVStream *s);
#if FF_API_GET_END_PTS
@ -1726,6 +1803,26 @@ typedef struct AVFormatContext {
* @return 0 on success, a negative AVERROR code on failure
*/
int (*io_close2)(struct AVFormatContext *s, AVIOContext *pb);
/**
* Number of elements in AVFormatContext.stream_groups.
*
* Set by avformat_stream_group_create(), must not be modified by any other code.
*/
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().
*
* - demuxing: groups may be created by libavformat in avformat_open_input().
* If AVFMTCTX_NOHEADER is set in ctx_flags, then new groups may also
* appear in av_read_frame().
* - muxing: groups may be created by the user before avformat_write_header().
*
* Freed by libavformat in avformat_free_context().
*/
AVStreamGroup **stream_groups;
} AVFormatContext;
/**
@ -1844,6 +1941,37 @@ const AVClass *avformat_get_class(void);
*/
const AVClass *av_stream_get_class(void);
/**
* Get the AVClass for AVStreamGroup. It can be used in combination with
* AV_OPT_SEARCH_FAKE_OBJ for examining options.
*
* @see av_opt_find().
*/
const AVClass *av_stream_group_get_class(void);
/**
* Add a new empty stream group to a media file.
*
* 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().
*
* User is required to call avformat_free_context() to clean up the allocation
* by avformat_stream_group_create().
*
* New streams can be added to the group with avformat_stream_group_add_stream().
*
* @param s media file handle
*
* @return newly created group or NULL on error.
* @see avformat_new_stream, avformat_stream_group_add_stream.
*/
AVStreamGroup *avformat_stream_group_create(AVFormatContext *s,
enum AVStreamGroupParamsType type,
AVDictionary **options);
/**
* Add a new stream to a media file.
*
@ -1863,6 +1991,31 @@ const AVClass *av_stream_get_class(void);
*/
AVStream *avformat_new_stream(AVFormatContext *s, const struct AVCodec *c);
/**
* Add an already allocated stream to a 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 new group with avformat_stream_group_create() and stream with
* avformat_new_stream().
*
* User is required to call avformat_free_context() to clean up the allocation
* by avformat_stream_group_add_stream().
*
* @param stg stream group belonging to a media file.
* @param st stream in the media file to add to the group.
*
* @retval 0 success
* @retval AVERROR(EEXIST) the stream was already in the group
* @retval "another negative error code" legitimate errors
*
* @see avformat_new_stream, avformat_stream_group_create.
*/
int avformat_stream_group_add_stream(AVStreamGroup *stg, AVStream *st);
#if FF_API_AVSTREAM_SIDE_DATA
/**
* Wrap an existing array as stream side data.

View File

@ -24,6 +24,7 @@
#include "libavutil/channel_layout.h"
#include "libavutil/display.h"
#include "libavutil/iamf.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/log.h"
#include "libavutil/mastering_display_metadata.h"
@ -134,28 +135,36 @@ static void print_fps(double d, const char *postfix)
av_log(NULL, AV_LOG_INFO, "%1.0fk %s", d / 1000, postfix);
}
static void dump_dictionary(void *ctx, const AVDictionary *m,
const char *name, const char *indent)
{
const AVDictionaryEntry *tag = NULL;
if (!m)
return;
av_log(ctx, AV_LOG_INFO, "%s%s:\n", indent, name);
while ((tag = av_dict_iterate(m, tag)))
if (strcmp("language", tag->key)) {
const char *p = tag->value;
av_log(ctx, AV_LOG_INFO,
"%s %-16s: ", indent, tag->key);
while (*p) {
size_t len = strcspn(p, "\x8\xa\xb\xc\xd");
av_log(ctx, AV_LOG_INFO, "%.*s", (int)(FFMIN(255, len)), p);
p += len;
if (*p == 0xd) av_log(ctx, AV_LOG_INFO, " ");
if (*p == 0xa) av_log(ctx, AV_LOG_INFO, "\n%s %-16s: ", indent, "");
if (*p) p++;
}
av_log(ctx, AV_LOG_INFO, "\n");
}
}
static void dump_metadata(void *ctx, const AVDictionary *m, const char *indent)
{
if (m && !(av_dict_count(m) == 1 && av_dict_get(m, "language", NULL, 0))) {
const AVDictionaryEntry *tag = NULL;
av_log(ctx, AV_LOG_INFO, "%sMetadata:\n", indent);
while ((tag = av_dict_iterate(m, tag)))
if (strcmp("language", tag->key)) {
const char *p = tag->value;
av_log(ctx, AV_LOG_INFO,
"%s %-16s: ", indent, tag->key);
while (*p) {
size_t len = strcspn(p, "\x8\xa\xb\xc\xd");
av_log(ctx, AV_LOG_INFO, "%.*s", (int)(FFMIN(255, len)), p);
p += len;
if (*p == 0xd) av_log(ctx, AV_LOG_INFO, " ");
if (*p == 0xa) av_log(ctx, AV_LOG_INFO, "\n%s %-16s: ", indent, "");
if (*p) p++;
}
av_log(ctx, AV_LOG_INFO, "\n");
}
}
if (m && !(av_dict_count(m) == 1 && av_dict_get(m, "language", NULL, 0)))
dump_dictionary(ctx, m, "Metadata", indent);
}
/* param change side data*/
@ -509,7 +518,7 @@ static void dump_sidedata(void *ctx, const AVStream *st, const char *indent)
/* "user interface" functions */
static void dump_stream_format(const AVFormatContext *ic, int i,
int index, int is_output)
int group_index, int index, int is_output)
{
char buf[256];
int flags = (is_output ? ic->oformat->flags : ic->iformat->flags);
@ -517,6 +526,8 @@ static void dump_stream_format(const AVFormatContext *ic, int i,
const FFStream *const sti = cffstream(st);
const AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL, 0);
const char *separator = ic->dump_separator;
const char *group_indent = group_index >= 0 ? " " : "";
const char *extra_indent = group_index >= 0 ? " " : " ";
AVCodecContext *avctx;
int ret;
@ -543,7 +554,8 @@ static void dump_stream_format(const AVFormatContext *ic, int i,
avcodec_string(buf, sizeof(buf), avctx, is_output);
avcodec_free_context(&avctx);
av_log(NULL, AV_LOG_INFO, " Stream #%d:%d", index, i);
av_log(NULL, AV_LOG_INFO, "%s Stream #%d", group_indent, index);
av_log(NULL, AV_LOG_INFO, ":%d", i);
/* the pid is an important information, so we display it */
/* XXX: add a generic system */
@ -621,9 +633,89 @@ static void dump_stream_format(const AVFormatContext *ic, int i,
av_log(NULL, AV_LOG_INFO, " (non-diegetic)");
av_log(NULL, AV_LOG_INFO, "\n");
dump_metadata(NULL, st->metadata, " ");
dump_metadata(NULL, st->metadata, extra_indent);
dump_sidedata(NULL, st, " ");
dump_sidedata(NULL, st, extra_indent);
}
static void dump_stream_group(const AVFormatContext *ic, uint8_t *printed,
int i, int index, int is_output)
{
const AVStreamGroup *stg = ic->stream_groups[i];
int flags = (is_output ? ic->oformat->flags : ic->iformat->flags);
char buf[512];
int ret;
av_log(NULL, AV_LOG_INFO, " Stream group #%d:%d", index, i);
if (flags & AVFMT_SHOW_IDS)
av_log(NULL, AV_LOG_INFO, "[0x%"PRIx64"]", stg->id);
av_log(NULL, AV_LOG_INFO, ":");
switch (stg->type) {
case AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT: {
const AVIAMFAudioElement *audio_element = stg->params.iamf_audio_element;
av_log(NULL, AV_LOG_INFO, " IAMF Audio Element\n");
dump_metadata(NULL, stg->metadata, " ");
for (int j = 0; j < audio_element->nb_layers; j++) {
const AVIAMFLayer *layer = audio_element->layers[j];
int channel_count = layer->ch_layout.nb_channels;
av_log(NULL, AV_LOG_INFO, " Layer %d:", j);
ret = av_channel_layout_describe(&layer->ch_layout, buf, sizeof(buf));
if (ret >= 0)
av_log(NULL, AV_LOG_INFO, " %s", buf);
av_log(NULL, AV_LOG_INFO, "\n");
for (int k = 0; channel_count > 0 && k < stg->nb_streams; k++) {
AVStream *st = stg->streams[k];
dump_stream_format(ic, st->index, i, index, is_output);
printed[st->index] = 1;
channel_count -= st->codecpar->ch_layout.nb_channels;
}
}
break;
}
case AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION: {
const AVIAMFMixPresentation *mix_presentation = stg->params.iamf_mix_presentation;
av_log(NULL, AV_LOG_INFO, " IAMF Mix Presentation\n");
dump_metadata(NULL, stg->metadata, " ");
dump_dictionary(NULL, mix_presentation->annotations, "Annotations", " ");
for (int j = 0; j < mix_presentation->nb_submixes; j++) {
AVIAMFSubmix *sub_mix = mix_presentation->submixes[j];
av_log(NULL, AV_LOG_INFO, " Submix %d:\n", j);
for (int k = 0; k < sub_mix->nb_elements; k++) {
const AVIAMFSubmixElement *submix_element = sub_mix->elements[k];
const AVStreamGroup *audio_element = NULL;
for (int l = 0; l < ic->nb_stream_groups; l++)
if (ic->stream_groups[l]->type == AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT &&
ic->stream_groups[l]->id == submix_element->audio_element_id) {
audio_element = ic->stream_groups[l];
break;
}
if (audio_element) {
av_log(NULL, AV_LOG_INFO, " IAMF Audio Element #%d:%d",
index, audio_element->index);
if (flags & AVFMT_SHOW_IDS)
av_log(NULL, AV_LOG_INFO, "[0x%"PRIx64"]", audio_element->id);
av_log(NULL, AV_LOG_INFO, "\n");
dump_dictionary(NULL, submix_element->annotations, "Annotations", " ");
}
}
for (int k = 0; k < sub_mix->nb_layouts; k++) {
const AVIAMFSubmixLayout *submix_layout = sub_mix->layouts[k];
av_log(NULL, AV_LOG_INFO, " Layout #%d:", k);
if (submix_layout->layout_type == 2) {
ret = av_channel_layout_describe(&submix_layout->sound_system, buf, sizeof(buf));
if (ret >= 0)
av_log(NULL, AV_LOG_INFO, " %s", buf);
} else if (submix_layout->layout_type == 3)
av_log(NULL, AV_LOG_INFO, " Binaural");
av_log(NULL, AV_LOG_INFO, "\n");
}
}
break;
}
default:
break;
}
}
void av_dump_format(AVFormatContext *ic, int index,
@ -699,7 +791,7 @@ void av_dump_format(AVFormatContext *ic, int index,
dump_metadata(NULL, program->metadata, " ");
for (k = 0; k < program->nb_stream_indexes; k++) {
dump_stream_format(ic, program->stream_index[k],
index, is_output);
-1, index, is_output);
printed[program->stream_index[k]] = 1;
}
total += program->nb_stream_indexes;
@ -708,9 +800,12 @@ void av_dump_format(AVFormatContext *ic, int index,
av_log(NULL, AV_LOG_INFO, " No Program\n");
}
for (i = 0; i < ic->nb_stream_groups; i++)
dump_stream_group(ic, printed, i, index, is_output);
for (i = 0; i < ic->nb_streams; i++)
if (!printed[i])
dump_stream_format(ic, i, index, is_output);
dump_stream_format(ic, i, -1, index, is_output);
av_free(printed);
}

View File

@ -202,6 +202,7 @@ typedef struct FFStream {
*/
AVStream pub;
AVFormatContext *fmtctx;
/**
* Set to 1 if the codec allows reordering, so pts can be different
* from dts.
@ -427,6 +428,26 @@ static av_always_inline const FFStream *cffstream(const AVStream *st)
return (const FFStream*)st;
}
typedef struct FFStreamGroup {
/**
* The public context.
*/
AVStreamGroup pub;
AVFormatContext *fmtctx;
} FFStreamGroup;
static av_always_inline FFStreamGroup *ffstreamgroup(AVStreamGroup *stg)
{
return (FFStreamGroup*)stg;
}
static av_always_inline const FFStreamGroup *cffstreamgroup(const AVStreamGroup *stg)
{
return (const FFStreamGroup*)stg;
}
#ifdef __GNUC__
#define dynarray_add(tab, nb_ptr, elem)\
do {\
@ -608,6 +629,18 @@ void ff_free_stream(AVStream **st);
*/
void ff_remove_stream(AVFormatContext *s, AVStream *st);
/**
* Frees a stream group without modifying the corresponding AVFormatContext.
* Must only be called if the latter doesn't matter or if the stream
* is not yet attached to an AVFormatContext.
*/
void ff_free_stream_group(AVStreamGroup **pstg);
/**
* Remove a stream group from its AVFormatContext and free it.
* The group must be the last stream of the AVFormatContext.
*/
void ff_remove_stream_group(AVFormatContext *s, AVStreamGroup *stg);
unsigned int ff_codec_get_tag(const AVCodecTag *tags, enum AVCodecID id);
enum AVCodecID ff_codec_get_id(const AVCodecTag *tags, unsigned int tag);

View File

@ -26,6 +26,7 @@
#include "libavcodec/codec_par.h"
#include "libavutil/avassert.h"
#include "libavutil/iamf.h"
#include "libavutil/internal.h"
#include "libavutil/intmath.h"
#include "libavutil/opt.h"
@ -271,6 +272,7 @@ AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c)
if (!st->codecpar)
goto fail;
sti->fmtctx = s;
sti->avctx = avcodec_alloc_context3(NULL);
if (!sti->avctx)
goto fail;
@ -325,6 +327,143 @@ fail:
return NULL;
}
static void *stream_group_child_next(void *obj, void *prev)
{
AVStreamGroup *stg = obj;
if (!prev) {
switch(stg->type) {
case AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT:
return stg->params.iamf_audio_element;
case AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION:
return stg->params.iamf_mix_presentation;
default:
break;
}
}
return NULL;
}
static const AVClass *stream_group_child_iterate(void **opaque)
{
uintptr_t i = (uintptr_t)*opaque;
const AVClass *ret = NULL;
switch(i) {
case AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT:
ret = av_iamf_audio_element_get_class();
break;
case AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION:
ret = av_iamf_mix_presentation_get_class();
break;
default:
break;
}
if (ret)
*opaque = (void*)(i + 1);
return ret;
}
static const AVOption stream_group_options[] = {
{"id", "Set group id", offsetof(AVStreamGroup, id), AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, AV_OPT_FLAG_ENCODING_PARAM },
{ NULL }
};
static const AVClass stream_group_class = {
.class_name = "AVStreamGroup",
.item_name = av_default_item_name,
.version = LIBAVUTIL_VERSION_INT,
.option = stream_group_options,
.child_next = stream_group_child_next,
.child_class_iterate = stream_group_child_iterate,
};
const AVClass *av_stream_group_get_class(void)
{
return &stream_group_class;
}
AVStreamGroup *avformat_stream_group_create(AVFormatContext *s,
enum AVStreamGroupParamsType type,
AVDictionary **options)
{
AVStreamGroup **stream_groups;
AVStreamGroup *stg;
FFStreamGroup *stgi;
stream_groups = av_realloc_array(s->stream_groups, s->nb_stream_groups + 1,
sizeof(*stream_groups));
if (!stream_groups)
return NULL;
s->stream_groups = stream_groups;
stgi = av_mallocz(sizeof(*stgi));
if (!stgi)
return NULL;
stg = &stgi->pub;
stg->av_class = &stream_group_class;
av_opt_set_defaults(stg);
stg->type = type;
switch (type) {
case AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT:
stg->params.iamf_audio_element = av_iamf_audio_element_alloc();
if (!stg->params.iamf_audio_element)
goto fail;
break;
case AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION:
stg->params.iamf_mix_presentation = av_iamf_mix_presentation_alloc();
if (!stg->params.iamf_mix_presentation)
goto fail;
break;
default:
goto fail;
}
if (options) {
if (av_opt_set_dict2(stg, options, AV_OPT_SEARCH_CHILDREN))
goto fail;
}
stgi->fmtctx = s;
stg->index = s->nb_stream_groups;
s->stream_groups[s->nb_stream_groups++] = stg;
return stg;
fail:
ff_free_stream_group(&stg);
return NULL;
}
static int stream_group_add_stream(AVStreamGroup *stg, AVStream *st)
{
AVStream **streams = av_realloc_array(stg->streams, stg->nb_streams + 1,
sizeof(*stg->streams));
if (!streams)
return AVERROR(ENOMEM);
stg->streams = streams;
stg->streams[stg->nb_streams++] = st;
return 0;
}
int avformat_stream_group_add_stream(AVStreamGroup *stg, AVStream *st)
{
const FFStreamGroup *stgi = cffstreamgroup(stg);
const FFStream *sti = cffstream(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)
return AVERROR(EEXIST);
return stream_group_add_stream(stg, st);
}
static int option_is_disposition(const AVOption *opt)
{
return opt->type == AV_OPT_TYPE_CONST &&

View File

@ -31,7 +31,7 @@
#include "version_major.h"
#define LIBAVFORMAT_VERSION_MINOR 18
#define LIBAVFORMAT_VERSION_MINOR 19
#define LIBAVFORMAT_VERSION_MICRO 100
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \