[FFmpeg-devel] [PATCH 3/8] ffmpeg: add support for muxing AVStreamGroups

2023-12-15 Thread James Almer
Starting with IAMF support.

Signed-off-by: James Almer 
---
 doc/ffmpeg.texi   | 200 ++
 fftools/ffmpeg.h  |   2 +
 fftools/ffmpeg_mux_init.c | 342 ++
 fftools/ffmpeg_opt.c  |   2 +
 4 files changed, 546 insertions(+)

diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi
index c503963941..1fadb20686 100644
--- a/doc/ffmpeg.texi
+++ b/doc/ffmpeg.texi
@@ -623,6 +623,206 @@ Not all muxers support embedded thumbnails, and those who 
do, only support a few
 Creates a program with the specified @var{title}, @var{program_num} and adds 
the specified
 @var{stream}(s) to it.
 
+@item -stream_group 
type=@var{type}:st=@var{stream}[:st=@var{stream}][:stg=@var{stream_group}][:id=@var{stream_group_id}...]
 (@emph{output})
+
+Creates a stream group of the specified @var{type}, @var{stream_group_id} and 
adds the specified
+@var{stream}(s) and/or previously defined @var{stream_group}(s) to it.
+
+@var{type} can be one of the following:
+@table @option
+
+@item iamf_audio_element
+Groups @var{stream}s that belong to the same IAMF Audio Element
+
+For this group @var{type}, the following options are available
+@table @option
+@item audio_element_type
+The Audio Element type. The following values are supported:
+
+@table @option
+@item channel
+Scalable channel audio representation
+@item scene
+Ambisonics representation
+@end table
+
+@item demixing
+Demixing information used to reconstruct a scalable channel audio 
representation.
+This option must be separated from the rest with a ',', and takes the following
+key=value options
+
+@table @option
+@item parameter_id
+An identifier parameters blocks in frames may refer to
+@item dmixp_mode
+A pre-defined combination of demixing parameters
+@end table
+
+@item recon_gain
+Recon gain information used to reconstruct a scalable channel audio 
representation.
+This option must be separated from the rest with a ',', and takes the following
+key=value options
+
+@table @option
+@item parameter_id
+An identifier parameters blocks in frames may refer to
+@end table
+
+@item layer
+A layer defining a Channel Layout in the Audio Element.
+This option must be separated from the rest with a ','. Several ',' separated 
entries
+can be defined, and at least one must be set.
+
+It takes the following ":"-separated key=value options
+
+@table @option
+@item ch_layout
+The layer's channel layout
+@item flags
+The following flags are available:
+
+@table @option
+@item recon_gain
+Wether to signal if recon_gain is present as metadata in parameter blocks 
within frames
+@end table
+
+@item output_gain
+@item output_gain_flags
+Which channels output_gain applies to. The following flags are available:
+
+@table @option
+@item FL
+@item FR
+@item BL
+@item BR
+@item TFL
+@item TFR
+@end table
+
+@item ambisonics_mode
+The ambisonics mode. This has no effect if audio_element_type is set to 
channel.
+
+The following values are supported:
+
+@table @option
+@item mono
+Each ambisonics channel is coded as an individual mono stream in the group
+@end table
+
+@end table
+
+@item default_w
+Default weight value
+
+@end table
+
+@item iamf_mix_presentation
+Groups @var{stream}s that belong to all IAMF Audio Element the same
+IAMF Mix Presentation references
+
+For this group @var{type}, the following options are available
+
+@table @option
+@item submix
+A sub-mix within the Mix Presentation.
+This option must be separated from the rest with a ','. Several ',' separated 
entries
+can be defined, and at least one must be set.
+
+It takes the following ":"-separated key=value options
+
+@table @option
+@item parameter_id
+An identifier parameters blocks in frames may refer to, for post-processing 
the mixed
+audio signal to generate the audio signal for playback
+@item parameter_rate
+The sample rate duration fields in parameters blocks in frames that refer to 
this
+@var{parameter_id} are expressed as
+@item default_mix_gain
+Default mix gain value to apply when there are no parameter blocks sharing the 
same
+@var{parameter_id} for a given frame
+
+@item element
+References an Audio Element used in this Mix Presentation to generate the 
final output
+audio signal for playback.
+This option must be separated from the rest with a '|'. Several '|' separated 
entries
+can be defined, and at least one must be set.
+
+It takes the following ":"-separated key=value options:
+
+@table @option
+@item stg
+The @var{stream_group_id} for an Audio Element which this sub-mix refers to
+@item parameter_id
+An identifier parameters blocks in frames may refer to, for applying any 
processing to
+the referenced and rendered Audio Element before being summed with other 
processed Audio
+Elements
+@item parameter_rate
+The sample rate duration fields in parameters blocks in frames that refer to 
this
+@var{parameter_id} are expressed as
+@item default_mix_gain
+Default mix gain value to apply when there are no parameter blocks sharing the 
same

[FFmpeg-devel] [PATCH 3/8] ffmpeg: add support for muxing AVStreamGroups

2023-12-14 Thread James Almer
Starting with IAMF support.

Signed-off-by: James Almer 
---
 doc/ffmpeg.texi   | 200 ++
 fftools/ffmpeg.h  |   2 +
 fftools/ffmpeg_mux_init.c | 341 ++
 fftools/ffmpeg_opt.c  |   2 +
 4 files changed, 545 insertions(+)

diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi
index c503963941..1fadb20686 100644
--- a/doc/ffmpeg.texi
+++ b/doc/ffmpeg.texi
@@ -623,6 +623,206 @@ Not all muxers support embedded thumbnails, and those who 
do, only support a few
 Creates a program with the specified @var{title}, @var{program_num} and adds 
the specified
 @var{stream}(s) to it.
 
+@item -stream_group 
type=@var{type}:st=@var{stream}[:st=@var{stream}][:stg=@var{stream_group}][:id=@var{stream_group_id}...]
 (@emph{output})
+
+Creates a stream group of the specified @var{type}, @var{stream_group_id} and 
adds the specified
+@var{stream}(s) and/or previously defined @var{stream_group}(s) to it.
+
+@var{type} can be one of the following:
+@table @option
+
+@item iamf_audio_element
+Groups @var{stream}s that belong to the same IAMF Audio Element
+
+For this group @var{type}, the following options are available
+@table @option
+@item audio_element_type
+The Audio Element type. The following values are supported:
+
+@table @option
+@item channel
+Scalable channel audio representation
+@item scene
+Ambisonics representation
+@end table
+
+@item demixing
+Demixing information used to reconstruct a scalable channel audio 
representation.
+This option must be separated from the rest with a ',', and takes the following
+key=value options
+
+@table @option
+@item parameter_id
+An identifier parameters blocks in frames may refer to
+@item dmixp_mode
+A pre-defined combination of demixing parameters
+@end table
+
+@item recon_gain
+Recon gain information used to reconstruct a scalable channel audio 
representation.
+This option must be separated from the rest with a ',', and takes the following
+key=value options
+
+@table @option
+@item parameter_id
+An identifier parameters blocks in frames may refer to
+@end table
+
+@item layer
+A layer defining a Channel Layout in the Audio Element.
+This option must be separated from the rest with a ','. Several ',' separated 
entries
+can be defined, and at least one must be set.
+
+It takes the following ":"-separated key=value options
+
+@table @option
+@item ch_layout
+The layer's channel layout
+@item flags
+The following flags are available:
+
+@table @option
+@item recon_gain
+Wether to signal if recon_gain is present as metadata in parameter blocks 
within frames
+@end table
+
+@item output_gain
+@item output_gain_flags
+Which channels output_gain applies to. The following flags are available:
+
+@table @option
+@item FL
+@item FR
+@item BL
+@item BR
+@item TFL
+@item TFR
+@end table
+
+@item ambisonics_mode
+The ambisonics mode. This has no effect if audio_element_type is set to 
channel.
+
+The following values are supported:
+
+@table @option
+@item mono
+Each ambisonics channel is coded as an individual mono stream in the group
+@end table
+
+@end table
+
+@item default_w
+Default weight value
+
+@end table
+
+@item iamf_mix_presentation
+Groups @var{stream}s that belong to all IAMF Audio Element the same
+IAMF Mix Presentation references
+
+For this group @var{type}, the following options are available
+
+@table @option
+@item submix
+A sub-mix within the Mix Presentation.
+This option must be separated from the rest with a ','. Several ',' separated 
entries
+can be defined, and at least one must be set.
+
+It takes the following ":"-separated key=value options
+
+@table @option
+@item parameter_id
+An identifier parameters blocks in frames may refer to, for post-processing 
the mixed
+audio signal to generate the audio signal for playback
+@item parameter_rate
+The sample rate duration fields in parameters blocks in frames that refer to 
this
+@var{parameter_id} are expressed as
+@item default_mix_gain
+Default mix gain value to apply when there are no parameter blocks sharing the 
same
+@var{parameter_id} for a given frame
+
+@item element
+References an Audio Element used in this Mix Presentation to generate the 
final output
+audio signal for playback.
+This option must be separated from the rest with a '|'. Several '|' separated 
entries
+can be defined, and at least one must be set.
+
+It takes the following ":"-separated key=value options:
+
+@table @option
+@item stg
+The @var{stream_group_id} for an Audio Element which this sub-mix refers to
+@item parameter_id
+An identifier parameters blocks in frames may refer to, for applying any 
processing to
+the referenced and rendered Audio Element before being summed with other 
processed Audio
+Elements
+@item parameter_rate
+The sample rate duration fields in parameters blocks in frames that refer to 
this
+@var{parameter_id} are expressed as
+@item default_mix_gain
+Default mix gain value to apply when there are no parameter blocks sharing the 
same

Re: [FFmpeg-devel] [PATCH 3/8] ffmpeg: add support for muxing AVStreamGroups

2023-12-12 Thread Anton Khirnov
Quoting James Almer (2023-12-11 13:46:36)
> AVStreamGroup.type is not setteable through AVOptions, but it of course 
> needs to be supported by the CLI. So i catch it and remove it from the 
> dict that will be used for avformat_stream_group_create().
> 
> I can change the comment to "'type' is not a user settable AVOption".

That seems better, thanks.

> > 3) Print the string, not the index.
> > 
> >> +ret = AVERROR(EINVAL);
> >> +goto end;
> >> +}
> >> +
> >> +ret = av_opt_eval_int(, opts, e->value, );
> >> +if (ret < 0 || type == AV_STREAM_GROUP_PARAMS_NONE) {
> >> +av_log(mux, AV_LOG_ERROR, "Invalid group type \"%s\"\n", 
> >> e->value);
> >> +goto end;
> >> +}
> >> +
> >> +av_dict_copy(, dict, 0);
> >> +stg = avformat_stream_group_create(oc, type, );
> >> +if (!stg) {
> >> +ret = AVERROR(ENOMEM);
> >> +goto end;
> >> +}
> >> +av_dict_set(, "type", NULL, 0);
> >> +
> >> +e = NULL;
> >> +while (e = av_dict_get(dict, "st", e, 0)) {
> >> +unsigned int idx = strtol(e->value, NULL, 0);
> >> +if (idx >= oc->nb_streams) {
> >> +av_log(mux, AV_LOG_ERROR, "Invalid stream index 
> >> %d\n", idx);
> >> +ret = AVERROR(EINVAL);
> >> +goto end;
> >> +}
> > 
> > This block seems confused about signedness of e->value.
> 
> You mean change %d to %u?

I mean strtol will parse the string into a signed number, then you
assign the result into unsigned, and print it as signed. It's probably
more user-friendly to keep parsing it as signed, and add a check for
idx >= 0.

-- 
Anton Khirnov
___
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".


Re: [FFmpeg-devel] [PATCH 3/8] ffmpeg: add support for muxing AVStreamGroups

2023-12-11 Thread James Almer

On 12/11/2023 8:48 AM, Anton Khirnov wrote:

Quoting James Almer (2023-12-05 23:43:57)

Starting with IAMF support.

Signed-off-by: James Almer 
---
  fftools/ffmpeg.h  |   2 +
  fftools/ffmpeg_mux_init.c | 335 ++
  fftools/ffmpeg_opt.c  |   2 +
  3 files changed, 339 insertions(+)


Missing documentation.


Will do.




+static int of_add_groups(Muxer *mux, const OptionsContext *o)
+{
+AVFormatContext *oc = mux->fc;
+int ret;
+
+/* process manually set groups */
+for (int i = 0; i < o->nb_stream_groups; i++) {
+AVDictionary *dict = NULL, *tmp = NULL;
+const AVDictionaryEntry *e;
+AVStreamGroup *stg = NULL;
+int type;
+const char *token;
+char *str, *ptr = NULL;
+const AVOption opts[] = {
+{ "type", "Set group type", offsetof(AVStreamGroup, type), 
AV_OPT_TYPE_INT,
+{ .i64 = 0 }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, 
"type" },
+{ "iamf_audio_element",NULL, 0, AV_OPT_TYPE_CONST,
+{ .i64 = AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT },.unit = 
"type" },
+{ "iamf_mix_presentation", NULL, 0, AV_OPT_TYPE_CONST,
+{ .i64 = AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION }, .unit = 
"type" },
+{ NULL },
+};
+ const AVClass class = {
+.class_name = "StreamGroupType",
+.item_name  = av_default_item_name,
+.option = opts,
+.version= LIBAVUTIL_VERSION_INT,
+};
+const AVClass *pclass = 
+
+str = av_strdup(o->stream_groups[i].u.str);
+if (!str)
+goto end;
+
+token = av_strtok(str, ",", );
+if (token) {


Too many indentation levels, move this whole block into a separate
function.


+ret = av_dict_parse_string(, token, "=", ":", 
AV_DICT_MULTIKEY);
+if (ret < 0) {
+av_log(mux, AV_LOG_ERROR, "Error parsing group specification 
%s\n", token);
+goto end;
+}
+
+// "type" is not a user settable option in AVStreamGroup


This comment confuses me.


AVStreamGroup.type is not setteable through AVOptions, but it of course 
needs to be supported by the CLI. So i catch it and remove it from the 
dict that will be used for avformat_stream_group_create().


I can change the comment to "'type' is not a user settable AVOption".




+e = av_dict_get(dict, "type", NULL, 0);
+if (!e) {
+av_log(mux, AV_LOG_ERROR, "No type define for Steam Group 
%d\n", i);


1) Steam
2) defined? Or maybe specified.


Will change to specified.


3) Print the string, not the index.


+ret = AVERROR(EINVAL);
+goto end;
+}
+
+ret = av_opt_eval_int(, opts, e->value, );
+if (ret < 0 || type == AV_STREAM_GROUP_PARAMS_NONE) {
+av_log(mux, AV_LOG_ERROR, "Invalid group type \"%s\"\n", 
e->value);
+goto end;
+}
+
+av_dict_copy(, dict, 0);
+stg = avformat_stream_group_create(oc, type, );
+if (!stg) {
+ret = AVERROR(ENOMEM);
+goto end;
+}
+av_dict_set(, "type", NULL, 0);
+
+e = NULL;
+while (e = av_dict_get(dict, "st", e, 0)) {
+unsigned int idx = strtol(e->value, NULL, 0);
+if (idx >= oc->nb_streams) {
+av_log(mux, AV_LOG_ERROR, "Invalid stream index %d\n", 
idx);
+ret = AVERROR(EINVAL);
+goto end;
+}


This block seems confused about signedness of e->value.


You mean change %d to %u?




+avformat_stream_group_add_stream(stg, oc->streams[idx]);


Unchecked return value.



___
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".


Re: [FFmpeg-devel] [PATCH 3/8] ffmpeg: add support for muxing AVStreamGroups

2023-12-11 Thread Anton Khirnov
Quoting James Almer (2023-12-05 23:43:57)
> Starting with IAMF support.
> 
> Signed-off-by: James Almer 
> ---
>  fftools/ffmpeg.h  |   2 +
>  fftools/ffmpeg_mux_init.c | 335 ++
>  fftools/ffmpeg_opt.c  |   2 +
>  3 files changed, 339 insertions(+)

Missing documentation.

> +static int of_add_groups(Muxer *mux, const OptionsContext *o)
> +{
> +AVFormatContext *oc = mux->fc;
> +int ret;
> +
> +/* process manually set groups */
> +for (int i = 0; i < o->nb_stream_groups; i++) {
> +AVDictionary *dict = NULL, *tmp = NULL;
> +const AVDictionaryEntry *e;
> +AVStreamGroup *stg = NULL;
> +int type;
> +const char *token;
> +char *str, *ptr = NULL;
> +const AVOption opts[] = {
> +{ "type", "Set group type", offsetof(AVStreamGroup, type), 
> AV_OPT_TYPE_INT,
> +{ .i64 = 0 }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, 
> "type" },
> +{ "iamf_audio_element",NULL, 0, AV_OPT_TYPE_CONST,
> +{ .i64 = AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT },
> .unit = "type" },
> +{ "iamf_mix_presentation", NULL, 0, AV_OPT_TYPE_CONST,
> +{ .i64 = AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION }, 
> .unit = "type" },
> +{ NULL },
> +};
> + const AVClass class = {
> +.class_name = "StreamGroupType",
> +.item_name  = av_default_item_name,
> +.option = opts,
> +.version= LIBAVUTIL_VERSION_INT,
> +};
> +const AVClass *pclass = 
> +
> +str = av_strdup(o->stream_groups[i].u.str);
> +if (!str)
> +goto end;
> +
> +token = av_strtok(str, ",", );
> +if (token) {

Too many indentation levels, move this whole block into a separate
function.

> +ret = av_dict_parse_string(, token, "=", ":", 
> AV_DICT_MULTIKEY);
> +if (ret < 0) {
> +av_log(mux, AV_LOG_ERROR, "Error parsing group specification 
> %s\n", token);
> +goto end;
> +}
> +
> +// "type" is not a user settable option in AVStreamGroup

This comment confuses me.

> +e = av_dict_get(dict, "type", NULL, 0);
> +if (!e) {
> +av_log(mux, AV_LOG_ERROR, "No type define for Steam Group 
> %d\n", i);

1) Steam
2) defined? Or maybe specified.
3) Print the string, not the index.

> +ret = AVERROR(EINVAL);
> +goto end;
> +}
> +
> +ret = av_opt_eval_int(, opts, e->value, );
> +if (ret < 0 || type == AV_STREAM_GROUP_PARAMS_NONE) {
> +av_log(mux, AV_LOG_ERROR, "Invalid group type \"%s\"\n", 
> e->value);
> +goto end;
> +}
> +
> +av_dict_copy(, dict, 0);
> +stg = avformat_stream_group_create(oc, type, );
> +if (!stg) {
> +ret = AVERROR(ENOMEM);
> +goto end;
> +}
> +av_dict_set(, "type", NULL, 0);
> +
> +e = NULL;
> +while (e = av_dict_get(dict, "st", e, 0)) {
> +unsigned int idx = strtol(e->value, NULL, 0);
> +if (idx >= oc->nb_streams) {
> +av_log(mux, AV_LOG_ERROR, "Invalid stream index %d\n", 
> idx);
> +ret = AVERROR(EINVAL);
> +goto end;
> +}

This block seems confused about signedness of e->value.

> +avformat_stream_group_add_stream(stg, oc->streams[idx]);

Unchecked return value.


-- 
Anton Khirnov
___
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".


[FFmpeg-devel] [PATCH 3/8] ffmpeg: add support for muxing AVStreamGroups

2023-12-05 Thread James Almer
Starting with IAMF support.

Signed-off-by: James Almer 
---
 fftools/ffmpeg.h  |   2 +
 fftools/ffmpeg_mux_init.c | 335 ++
 fftools/ffmpeg_opt.c  |   2 +
 3 files changed, 339 insertions(+)

diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h
index 41935d39d5..057535adbb 100644
--- a/fftools/ffmpeg.h
+++ b/fftools/ffmpeg.h
@@ -262,6 +262,8 @@ typedef struct OptionsContext {
 intnb_disposition;
 SpecifierOpt *program;
 intnb_program;
+SpecifierOpt *stream_groups;
+intnb_stream_groups;
 SpecifierOpt *time_bases;
 intnb_time_bases;
 SpecifierOpt *enc_time_bases;
diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c
index 63a25a350f..7648f2a2f1 100644
--- a/fftools/ffmpeg_mux_init.c
+++ b/fftools/ffmpeg_mux_init.c
@@ -39,6 +39,7 @@
 #include "libavutil/dict.h"
 #include "libavutil/display.h"
 #include "libavutil/getenv_utf8.h"
+#include "libavutil/iamf.h"
 #include "libavutil/intreadwrite.h"
 #include "libavutil/log.h"
 #include "libavutil/mem.h"
@@ -1943,6 +1944,336 @@ static int setup_sync_queues(Muxer *mux, 
AVFormatContext *oc, int64_t buf_size_u
 return 0;
 }
 
+static int of_parse_iamf_audio_element_layers(Muxer *mux, AVStreamGroup *stg, 
char **ptr)
+{
+AVIAMFAudioElement *audio_element = stg->params.iamf_audio_element;
+AVDictionary *dict = NULL;
+const char *token;
+int ret = 0;
+
+audio_element->demixing_info =
+av_iamf_param_definition_alloc(AV_IAMF_PARAMETER_DEFINITION_DEMIXING, 
1, NULL);
+audio_element->recon_gain_info =
+
av_iamf_param_definition_alloc(AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN, 1, 
NULL);
+
+if (!audio_element->demixing_info ||
+!audio_element->recon_gain_info)
+return AVERROR(ENOMEM);
+
+/* process manually set layers and parameters */
+token = av_strtok(NULL, ",", ptr);
+while (token) {
+const AVDictionaryEntry *e;
+int demixing = 0, recon_gain = 0;
+int layer = 0;
+
+if (av_strstart(token, "layer=", ))
+layer = 1;
+else if (av_strstart(token, "demixing=", ))
+demixing = 1;
+else if (av_strstart(token, "recon_gain=", ))
+recon_gain = 1;
+
+av_dict_free();
+ret = av_dict_parse_string(, token, "=", ":", 0);
+if (ret < 0) {
+av_log(mux, AV_LOG_ERROR, "Error parsing audio element 
specification %s\n", token);
+goto fail;
+}
+
+if (layer) {
+AVIAMFLayer *audio_layer = 
av_iamf_audio_element_add_layer(audio_element);
+if (!audio_layer) {
+av_log(mux, AV_LOG_ERROR, "Error adding layer to stream group 
%d\n", stg->index);
+ret = AVERROR(ENOMEM);
+goto fail;
+}
+av_opt_set_dict(audio_layer, );
+} else if (demixing || recon_gain) {
+AVIAMFParamDefinition *param = demixing ? 
audio_element->demixing_info
+: 
audio_element->recon_gain_info;
+void *subblock = av_iamf_param_definition_get_subblock(param, 0);
+
+av_opt_set_dict(param, );
+av_opt_set_dict(subblock, );
+
+/* Hardcode spec parameters */
+param->param_definition_mode = 0;
+param->parameter_rate = stg->streams[0]->codecpar->sample_rate;
+param->duration =
+param->constant_subblock_duration = 
stg->streams[0]->codecpar->frame_size;
+}
+
+// make sure that no entries are left in the dict
+e = NULL;
+if (e = av_dict_iterate(dict, e)) {
+av_log(mux, AV_LOG_FATAL, "Unknown layer key %s.\n", e->key);
+ret = AVERROR(EINVAL);
+goto fail;
+}
+token = av_strtok(NULL, ",", ptr);
+}
+
+fail:
+av_dict_free();
+if (!ret && !audio_element->nb_layers) {
+av_log(mux, AV_LOG_ERROR, "No layer in audio element specification\n");
+ret = AVERROR(EINVAL);
+}
+
+return ret;
+}
+
+static int of_parse_iamf_submixes(Muxer *mux, AVStreamGroup *stg, char **ptr)
+{
+AVFormatContext *oc = mux->fc;
+AVIAMFMixPresentation *mix = stg->params.iamf_mix_presentation;
+AVDictionary *dict = NULL;
+const char *token;
+char *submix_str = NULL;
+int ret = 0;
+
+/* process manually set submixes */
+token = av_strtok(NULL, ",", ptr);
+while (token) {
+AVIAMFSubmix *submix = NULL;
+const char *subtoken;
+char *subptr = NULL;
+
+if (!av_strstart(token, "submix=", )) {
+av_log(mux, AV_LOG_ERROR, "No submix in mix presentation 
specification \"%s\"\n", token);
+goto fail;
+}
+
+submix_str = av_strdup(token);
+if (!submix_str)
+goto fail;
+
+submix = av_iamf_mix_presentation_add_submix(mix);
+if (!submix) {
+