Do this by extending the -stream_group option to accept a map key where the value selects the input file and stream group. The can and should set the streams in the output that the copied group will reference, same as they'd do if they created a group from scratch.
Example command line: ffmpeg -i input.iamf -map 0 -c:a copy -f null -stream_group \ map=0=0:st=0:st=1:st=2:st=3 -stream_group map=0=1:st=0:st=1:st=2:st=3 Signed-off-by: James Almer <jamr...@gmail.com> --- fftools/ffmpeg_mux_init.c | 154 +++++++++++++++++++++++++++++++++++++- 1 file changed, 152 insertions(+), 2 deletions(-) diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c index 6d8bd5bcdf..a46b0628d8 100644 --- a/fftools/ffmpeg_mux_init.c +++ b/fftools/ffmpeg_mux_init.c @@ -2232,11 +2232,137 @@ fail: return ret; } +static int of_serialize_options(Muxer *mux, void *obj, AVBPrint *bp) +{ + char *ptr; + int ret; + + ret = av_opt_serialize(obj, 0, AV_OPT_SERIALIZE_SKIP_DEFAULTS | AV_OPT_SERIALIZE_SEARCH_CHILDREN, + &ptr, '=', ':'); + if (ret < 0) { + av_log(mux, AV_LOG_ERROR, "Failed to serialize group\n"); + return ret; + } + + av_bprintf(bp, "%s", ptr); + ret = strlen(ptr); + av_free(ptr); + + return ret; +} + +#define SERIALIZE(parent, child) do { \ + ret = of_serialize_options(mux, parent->child, bp); \ + if (ret < 0) \ + return ret; \ +} while (0) + +#define SERIALIZE_LOOP(parent, child, suffix, separator) do { \ + for (int j = 0; j < parent->nb_## child ## suffix; j++) { \ + av_bprintf(bp, separator#child "="); \ + SERIALIZE(parent, child ## suffix[j]); \ + } \ +} while (0) + +static int64_t get_stream_group_index_from_id(Muxer *mux, int64_t id) +{ + AVFormatContext *oc = mux->fc; + + for (unsigned i = 0; i < oc->nb_stream_groups; i++) + if (oc->stream_groups[i]->id == id) + return oc->stream_groups[i]->index; + + return AVERROR(EINVAL); +} + +static int of_map_group(Muxer *mux, AVDictionary **dict, AVBPrint *bp, const char *map) +{ + AVStreamGroup *stg; + int ret, file_idx, stream_idx; + char *ptr; + + file_idx = strtol(map, &ptr, 0); + if (file_idx >= nb_input_files || file_idx < 0 || map == ptr) { + av_log(mux, AV_LOG_ERROR, "Invalid input file index: %d.\n", file_idx); + return AVERROR(EINVAL); + } + + stream_idx = strtol(*ptr == '=' ? ptr + 1 : ptr, &ptr, 0); + if (*ptr || stream_idx >= input_files[file_idx]->ctx->nb_stream_groups || stream_idx < 0) { + av_log(mux, AV_LOG_ERROR, "Invalid input stream group index: %d.\n", stream_idx); + return AVERROR(EINVAL); + } + + stg = input_files[file_idx]->ctx->stream_groups[stream_idx]; + ret = of_serialize_options(mux, stg, bp); + if (ret < 0) + return ret; + + ret = av_dict_parse_string(dict, bp->str, "=", ":", 0); + if (ret < 0) + av_log(mux, AV_LOG_ERROR, "Error parsing mapped group specification %s\n", ptr); + av_dict_set_int(dict, "type", stg->type, 0); + + av_log(mux, AV_LOG_VERBOSE, "stg %s\n", bp->str); + av_bprint_clear(bp); + switch(stg->type) { + case AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT: { + AVIAMFAudioElement *audio_element = stg->params.iamf_audio_element; + + if (audio_element->demixing_info) { + av_bprintf(bp, ",demixing="); + SERIALIZE(audio_element, demixing_info); + } + if (audio_element->recon_gain_info) { + av_bprintf(bp, ",recon_gain="); + SERIALIZE(audio_element, recon_gain_info); + } + SERIALIZE_LOOP(audio_element, layer, s, ","); + break; + } + case AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION: { + AVIAMFMixPresentation *mix = stg->params.iamf_mix_presentation; + + for (int i = 0; i < mix->nb_submixes; i++) { + AVIAMFSubmix *submix = mix->submixes[i]; + + av_bprintf(bp, ",submix="); + SERIALIZE(mix, submixes[i]); + for (int j = 0; j < submix->nb_elements; j++) { + AVIAMFSubmixElement *element = submix->elements[j]; + int64_t id = get_stream_group_index_from_id(mux, element->audio_element_id); + + if (id < 0) { + av_log(mux, AV_LOG_ERROR, "Invalid or missing stream group index in" + "submix element"); + return id; + } + + av_bprintf(bp, "|element="); + SERIALIZE(submix, elements[j]); + if (ret) + av_bprintf(bp, ":"); + av_bprintf(bp, "stg=%"PRId64, id); + } + SERIALIZE_LOOP(submix, layout, s, "|"); + } + break; + } + default: + av_log(mux, AV_LOG_ERROR, "Unsupported mapped group type %d.\n", stg->type); + ret = AVERROR(EINVAL); + break; + } + av_log(mux, AV_LOG_VERBOSE, "extra %s\n", bp->str); + return 0; +} + static int of_parse_group_token(Muxer *mux, const char *token, char *ptr) { AVFormatContext *oc = mux->fc; AVStreamGroup *stg; AVDictionary *dict = NULL, *tmp = NULL; + char *mapped_string = NULL; const AVDictionaryEntry *e; const AVOption opts[] = { { "type", "Set group type", offsetof(AVStreamGroup, type), AV_OPT_TYPE_INT, @@ -2262,8 +2388,31 @@ static int of_parse_group_token(Muxer *mux, const char *token, char *ptr) return ret; } + av_dict_copy(&tmp, dict, 0); + e = av_dict_get(dict, "map", NULL, 0); + if (e) { + AVBPrint bp; + + if (ptr) { + av_log(mux, AV_LOG_ERROR, "Unexpected extra parameters when mapping a" + " stream group\n"); + ret = AVERROR(EINVAL); + goto end; + } + + av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC); + ret = of_map_group(mux, &tmp, &bp, e->value); + if (ret < 0) { + av_bprint_finalize(&bp, NULL); + goto end; + } + + av_bprint_finalize(&bp, &mapped_string); + ptr = mapped_string; + } + // "type" is not a user settable AVOption in AVStreamGroup, so handle it here - e = av_dict_get(dict, "type", NULL, 0); + e = av_dict_get(tmp, "type", NULL, 0); if (!e) { av_log(mux, AV_LOG_ERROR, "No type specified for Stream Group in \"%s\"\n", token); ret = AVERROR(EINVAL); @@ -2278,7 +2427,6 @@ static int of_parse_group_token(Muxer *mux, const char *token, char *ptr) goto end; } - av_dict_copy(&tmp, dict, 0); stg = avformat_stream_group_create(oc, type, &tmp); if (!stg) { ret = AVERROR(ENOMEM); @@ -2331,6 +2479,7 @@ static int of_parse_group_token(Muxer *mux, const char *token, char *ptr) // make sure that nothing but "st" and "stg" entries are left in the dict e = NULL; + av_dict_set(&tmp, "map", NULL, 0); av_dict_set(&tmp, "type", NULL, 0); while (e = av_dict_iterate(tmp, e)) { if (!strcmp(e->key, "st") || !strcmp(e->key, "stg")) @@ -2343,6 +2492,7 @@ static int of_parse_group_token(Muxer *mux, const char *token, char *ptr) ret = 0; end: + av_free(mapped_string); av_dict_free(&dict); av_dict_free(&tmp); -- 2.44.0 _______________________________________________ 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".