mirror of
https://git.ffmpeg.org/ffmpeg.git
synced 2024-12-26 09:12:33 +00:00
avformat: introduce AVStreamGroup
Signed-off-by: James Almer <jamrial@gmail.com>
This commit is contained in:
parent
d2af93bbef
commit
556b596d1d
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 &&
|
||||
|
@ -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, \
|
||||
|
Loading…
Reference in New Issue
Block a user