[FFmpeg-devel] [PATCH 3/8] ffmpeg: add support for muxing AVStreamGroups
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
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
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
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
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
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) { +