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: 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 2023-12-18 - xxxxxxxxxxx - lavu 58.35.100 - iamf.h
Add a new API to support Immersive Audio Model and Formats. 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 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 additional stream specifier (see below), then it selects stream number
@var{stream_index} from the matching streams. Stream numbering is based on the @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 order of the streams as detected by libavformat except when a stream group
also specified. In this case it is based on the ordering of the streams in the specifier or program ID is also specified. In this case it is based on the
program. ordering of the streams in the group or program.
@item @var{stream_type}[:@var{additional_stream_specifier}] @item @var{stream_type}[:@var{additional_stream_specifier}]
@var{stream_type} is one of following: 'v' or 'V' for video, 'a' for audio, 's' @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 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 it matches streams which both have this type and match the
@var{additional_stream_specifier}. Otherwise, it matches all streams of the @var{additional_stream_specifier}. Otherwise, it matches all streams of the
specified type. 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}] @item p:@var{program_id}[:@var{additional_stream_specifier}]
Matches streams which are in the program with the id @var{program_id}. If 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 @var{additional_stream_specifier} is used, then it matches streams which both

View File

@ -24,6 +24,7 @@
#include "libavutil/avstring.h" #include "libavutil/avstring.h"
#include "libavutil/channel_layout.h" #include "libavutil/channel_layout.h"
#include "libavutil/frame.h" #include "libavutil/frame.h"
#include "libavutil/iamf.h"
#include "libavutil/intreadwrite.h" #include "libavutil/intreadwrite.h"
#include "libavutil/mem.h" #include "libavutil/mem.h"
#include "libavutil/opt.h" #include "libavutil/opt.h"
@ -80,6 +81,32 @@ FF_ENABLE_DEPRECATION_WARNINGS
av_freep(pst); 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) void ff_remove_stream(AVFormatContext *s, AVStream *st)
{ {
av_assert0(s->nb_streams>0); 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 ]); 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 */ /* XXX: suppress the packet queue */
void ff_flush_packet_queue(AVFormatContext *s) 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++) for (unsigned i = 0; i < s->nb_streams; i++)
ff_free_stream(&s->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; s->nb_streams = 0;
for (unsigned i = 0; i < s->nb_programs; i++) { 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->pkt);
av_packet_free(&si->parse_pkt); av_packet_free(&si->parse_pkt);
av_freep(&s->streams); av_freep(&s->streams);
av_freep(&s->stream_groups);
ff_flush_packet_queue(s); ff_flush_packet_queue(s);
av_freep(&s->url); av_freep(&s->url);
av_free(s); 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, static int match_stream_specifier(const AVFormatContext *s, const AVStream *st,
const char *spec, const char **indexptr, 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. */ int match = 1; /* Stores if the specifier matches so far. */
while (*spec) { while (*spec) {
@ -493,6 +532,46 @@ static int match_stream_specifier(const AVFormatContext *s, const AVStream *st,
match = 0; match = 0;
if (nopic && (st->disposition & AV_DISPOSITION_ATTACHED_PIC)) if (nopic && (st->disposition & AV_DISPOSITION_ATTACHED_PIC))
match = 0; 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) == ':') { } else if (*spec == 'p' && *(spec + 1) == ':') {
int prog_id; int prog_id;
int found = 0; int found = 0;
@ -591,10 +670,11 @@ int avformat_match_stream_specifier(AVFormatContext *s, AVStream *st,
int ret, index; int ret, index;
char *endptr; char *endptr;
const char *indexptr = NULL; const char *indexptr = NULL;
const AVStreamGroup *g = NULL;
const AVProgram *p = NULL; const AVProgram *p = NULL;
int nb_streams; 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) if (ret < 0)
goto error; goto error;
@ -612,10 +692,11 @@ int avformat_match_stream_specifier(AVFormatContext *s, AVStream *st,
return (index == st->index); return (index == st->index);
/* If we requested a matching stream index, we have to ensure st is that. */ /* 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++) { for (int i = 0; i < nb_streams && index >= 0; i++) {
const AVStream *candidate = s->streams[p ? p->stream_index[i] : i]; unsigned idx = g ? g->streams[i]->index : (p ? p->stream_index[i] : i);
ret = match_stream_specifier(s, candidate, spec, NULL, NULL); const AVStream *candidate = s->streams[idx];
ret = match_stream_specifier(s, candidate, spec, NULL, NULL, NULL);
if (ret < 0) if (ret < 0)
goto error; goto error;
if (ret > 0 && index-- == 0 && st == candidate) if (ret > 0 && index-- == 0 && st == candidate)

View File

@ -1018,6 +1018,83 @@ typedef struct AVStream {
int pts_wrap_bits; int pts_wrap_bits;
} AVStream; } 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); struct AVCodecParserContext *av_stream_get_parser(const AVStream *s);
#if FF_API_GET_END_PTS #if FF_API_GET_END_PTS
@ -1726,6 +1803,26 @@ typedef struct AVFormatContext {
* @return 0 on success, a negative AVERROR code on failure * @return 0 on success, a negative AVERROR code on failure
*/ */
int (*io_close2)(struct AVFormatContext *s, AVIOContext *pb); 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; } AVFormatContext;
/** /**
@ -1844,6 +1941,37 @@ const AVClass *avformat_get_class(void);
*/ */
const AVClass *av_stream_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. * 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); 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 #if FF_API_AVSTREAM_SIDE_DATA
/** /**
* Wrap an existing array as stream side data. * Wrap an existing array as stream side data.

View File

@ -24,6 +24,7 @@
#include "libavutil/channel_layout.h" #include "libavutil/channel_layout.h"
#include "libavutil/display.h" #include "libavutil/display.h"
#include "libavutil/iamf.h"
#include "libavutil/intreadwrite.h" #include "libavutil/intreadwrite.h"
#include "libavutil/log.h" #include "libavutil/log.h"
#include "libavutil/mastering_display_metadata.h" #include "libavutil/mastering_display_metadata.h"
@ -134,12 +135,15 @@ static void print_fps(double d, const char *postfix)
av_log(NULL, AV_LOG_INFO, "%1.0fk %s", d / 1000, postfix); av_log(NULL, AV_LOG_INFO, "%1.0fk %s", d / 1000, postfix);
} }
static void dump_metadata(void *ctx, const AVDictionary *m, const char *indent) static void dump_dictionary(void *ctx, const AVDictionary *m,
const char *name, const char *indent)
{ {
if (m && !(av_dict_count(m) == 1 && av_dict_get(m, "language", NULL, 0))) {
const AVDictionaryEntry *tag = NULL; const AVDictionaryEntry *tag = NULL;
av_log(ctx, AV_LOG_INFO, "%sMetadata:\n", indent); if (!m)
return;
av_log(ctx, AV_LOG_INFO, "%s%s:\n", indent, name);
while ((tag = av_dict_iterate(m, tag))) while ((tag = av_dict_iterate(m, tag)))
if (strcmp("language", tag->key)) { if (strcmp("language", tag->key)) {
const char *p = tag->value; const char *p = tag->value;
@ -155,7 +159,12 @@ static void dump_metadata(void *ctx, const AVDictionary *m, const char *indent)
} }
av_log(ctx, AV_LOG_INFO, "\n"); 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)))
dump_dictionary(ctx, m, "Metadata", indent);
} }
/* param change side data*/ /* param change side data*/
@ -509,7 +518,7 @@ static void dump_sidedata(void *ctx, const AVStream *st, const char *indent)
/* "user interface" functions */ /* "user interface" functions */
static void dump_stream_format(const AVFormatContext *ic, int i, 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]; char buf[256];
int flags = (is_output ? ic->oformat->flags : ic->iformat->flags); 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 FFStream *const sti = cffstream(st);
const AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL, 0); const AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL, 0);
const char *separator = ic->dump_separator; const char *separator = ic->dump_separator;
const char *group_indent = group_index >= 0 ? " " : "";
const char *extra_indent = group_index >= 0 ? " " : " ";
AVCodecContext *avctx; AVCodecContext *avctx;
int ret; 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_string(buf, sizeof(buf), avctx, is_output);
avcodec_free_context(&avctx); 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 */ /* the pid is an important information, so we display it */
/* XXX: add a generic system */ /* 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, " (non-diegetic)");
av_log(NULL, AV_LOG_INFO, "\n"); 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, 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, " "); dump_metadata(NULL, program->metadata, " ");
for (k = 0; k < program->nb_stream_indexes; k++) { for (k = 0; k < program->nb_stream_indexes; k++) {
dump_stream_format(ic, program->stream_index[k], dump_stream_format(ic, program->stream_index[k],
index, is_output); -1, index, is_output);
printed[program->stream_index[k]] = 1; printed[program->stream_index[k]] = 1;
} }
total += program->nb_stream_indexes; 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"); 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++) for (i = 0; i < ic->nb_streams; i++)
if (!printed[i]) if (!printed[i])
dump_stream_format(ic, i, index, is_output); dump_stream_format(ic, i, -1, index, is_output);
av_free(printed); av_free(printed);
} }

View File

@ -202,6 +202,7 @@ typedef struct FFStream {
*/ */
AVStream pub; AVStream pub;
AVFormatContext *fmtctx;
/** /**
* Set to 1 if the codec allows reordering, so pts can be different * Set to 1 if the codec allows reordering, so pts can be different
* from dts. * from dts.
@ -427,6 +428,26 @@ static av_always_inline const FFStream *cffstream(const AVStream *st)
return (const FFStream*)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__ #ifdef __GNUC__
#define dynarray_add(tab, nb_ptr, elem)\ #define dynarray_add(tab, nb_ptr, elem)\
do {\ do {\
@ -608,6 +629,18 @@ void ff_free_stream(AVStream **st);
*/ */
void ff_remove_stream(AVFormatContext *s, 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); unsigned int ff_codec_get_tag(const AVCodecTag *tags, enum AVCodecID id);
enum AVCodecID ff_codec_get_id(const AVCodecTag *tags, unsigned int tag); enum AVCodecID ff_codec_get_id(const AVCodecTag *tags, unsigned int tag);

View File

@ -26,6 +26,7 @@
#include "libavcodec/codec_par.h" #include "libavcodec/codec_par.h"
#include "libavutil/avassert.h" #include "libavutil/avassert.h"
#include "libavutil/iamf.h"
#include "libavutil/internal.h" #include "libavutil/internal.h"
#include "libavutil/intmath.h" #include "libavutil/intmath.h"
#include "libavutil/opt.h" #include "libavutil/opt.h"
@ -271,6 +272,7 @@ AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c)
if (!st->codecpar) if (!st->codecpar)
goto fail; goto fail;
sti->fmtctx = s;
sti->avctx = avcodec_alloc_context3(NULL); sti->avctx = avcodec_alloc_context3(NULL);
if (!sti->avctx) if (!sti->avctx)
goto fail; goto fail;
@ -325,6 +327,143 @@ fail:
return NULL; 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) static int option_is_disposition(const AVOption *opt)
{ {
return opt->type == AV_OPT_TYPE_CONST && return opt->type == AV_OPT_TYPE_CONST &&

View File

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