This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit 3d667b147aab3bd4b1264a48ac293d994fc10c7c Author: Marton Balint <[email protected]> AuthorDate: Fri Nov 28 23:59:18 2025 +0100 Commit: Marton Balint <[email protected]> CommitDate: Sun Dec 7 19:36:49 2025 +0000 avfilter/af_amerge: add layout_mode option to control output channel layout Signed-off-by: Marton Balint <[email protected]> --- doc/filters.texi | 49 +++++++++++++++++++++++++++++++++++---------- libavfilter/af_amerge.c | 53 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 85 insertions(+), 17 deletions(-) diff --git a/doc/filters.texi b/doc/filters.texi index 168ea0d2da..464a602f8d 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -2436,6 +2436,11 @@ Only used if option named @var{start} is set to @code{-1}. Merge two or more audio streams into a single multi-channel stream. +All inputs must have the same sample rate, and format. + +If inputs do not have the same duration, the output will stop with the +shortest. + The filter accepts the following options: @table @option @@ -2443,15 +2448,25 @@ The filter accepts the following options: @item inputs Set the number of inputs. Default is 2. -@end table +@item layout_mode -If the channel layouts of the inputs are disjoint, and therefore compatible, -the channel layout of the output will be set accordingly and the channels -will be reordered as necessary. If the channel layouts of the inputs are not -disjoint, the output will have all the channels of the first input then all -the channels of the second input, in that order, and the channel layout of -the output will be the default value corresponding to the total number of -channels. +This option controls how the output channel layout is determined and if the +audio channels are reordered during merge. + +@table @option + +@item legacy + +This is the mode how the filter behaved historically so it is the default. + +If the channel layouts of the inputs are known and disjoint, and therefore +compatible, the channel layout of the output will be set accordingly and the +channels will be reordered as necessary. If the channel layouts of the inputs +are not disjoint, some of them are unknown, or they are using special channel +layouts, such as ambisonics, the output will have all the channels of the first +input then all the channels of the second input, in that order, and the channel +layout of the output will be the default value corresponding to the total +number of channels. For example, if the first input is in 2.1 (FL+FR+LF) and the second input is FC+BL+BR, then the output will be in 5.1, with the channels in the @@ -2462,10 +2477,22 @@ On the other hand, if both input are in stereo, the output channels will be in the default order: a1, a2, b1, b2, and the channel layout will be arbitrarily set to 4.0, which may or may not be the expected value. -All inputs must have the same sample rate, and format. +@item reset +This mode ignores the input channel layouts and does no channel reordering. +The output will have all the channels of the first input, then all the channels +of the second input, in that order, and so on. -If inputs do not have the same duration, the output will stop with the -shortest. +The output channel layout will only specify the total channel count. + +@item normal +This mode keeps channel name and designation information from the input +channels and does no channel reordering. The output will have all the channels +of the first input, then all the channels of the second input, in that order, +and so on. + +@end table + +@end table @subsection Examples diff --git a/libavfilter/af_amerge.c b/libavfilter/af_amerge.c index 92afb4691f..e300cf9c7b 100644 --- a/libavfilter/af_amerge.c +++ b/libavfilter/af_amerge.c @@ -23,6 +23,7 @@ * Audio merging filter */ +#include "libavutil/avassert.h" #include "libavutil/avstring.h" #include "libavutil/bprint.h" #include "libavutil/channel_layout.h" @@ -43,14 +44,27 @@ typedef struct AMergeContext { struct amerge_input { int nb_ch; /**< number of channels for the input */ } *in; + int layout_mode; /**< the method for determining the output channel layout */ } AMergeContext; #define OFFSET(x) offsetof(AMergeContext, x) #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +enum LayoutModes { + LM_LEGACY, + LM_RESET, + LM_NORMAL, + NB_LAYOUTMODES +}; + static const AVOption amerge_options[] = { { "inputs", "specify the number of inputs", OFFSET(nb_inputs), AV_OPT_TYPE_INT, { .i64 = 2 }, 1, SWR_CH_MAX, FLAGS }, + { "layout_mode", "method used to determine the output channel layout", OFFSET(layout_mode), + AV_OPT_TYPE_INT, { .i64 = LM_LEGACY }, 0, NB_LAYOUTMODES - 1, FLAGS, .unit = "layout_mode"}, + { "legacy", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = LM_LEGACY }, 0, 0, FLAGS, .unit = "layout_mode" }, + { "reset", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = LM_RESET }, 0, 0, FLAGS, .unit = "layout_mode" }, + { "normal", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = LM_NORMAL }, 0, 0, FLAGS, .unit = "layout_mode" }, { NULL } }; @@ -101,9 +115,16 @@ static int query_formats(AVFilterContext *ctx) av_log(ctx, AV_LOG_ERROR, "Too many channels (max %d)\n", SWR_CH_MAX); return AVERROR(EINVAL); } + ret = av_channel_layout_custom_init(&outlayout, nb_ch); + if (ret < 0) + return ret; for (int i = 0, ch_idx = 0; i < s->nb_inputs; i++) { for (int j = 0; j < s->in[i].nb_ch; j++) { enum AVChannel id = av_channel_layout_channel_from_index(INLAYOUT(ctx, i), j); + if (INLAYOUT(ctx, i)->order == AV_CHANNEL_ORDER_CUSTOM) + outlayout.u.map[ch_idx] = INLAYOUT(ctx, i)->u.map[j]; + else + outlayout.u.map[ch_idx].id = (id == AV_CHAN_NONE ? AV_CHAN_UNKNOWN : id); if (id >= 0 && id < 64) { outmask |= (1ULL << id); native_layout_routes[id] = ch_idx; @@ -112,6 +133,9 @@ static int query_formats(AVFilterContext *ctx) ch_idx++; } } + switch (s->layout_mode) { + case LM_LEGACY: + av_channel_layout_uninit(&outlayout); if (av_popcount64(outmask) != nb_ch) { av_log(ctx, AV_LOG_WARNING, "Input channel layouts overlap: " @@ -125,22 +149,39 @@ static int query_formats(AVFilterContext *ctx) s->route[native_layout_routes[c]] = ch_idx++; av_channel_layout_from_mask(&outlayout, outmask); } + break; + case LM_RESET: + av_channel_layout_uninit(&outlayout); + outlayout.order = AV_CHANNEL_ORDER_UNSPEC; + outlayout.nb_channels = nb_ch; + break; + case LM_NORMAL: + ret = av_channel_layout_retype(&outlayout, 0, AV_CHANNEL_LAYOUT_RETYPE_FLAG_CANONICAL); + if (ret < 0) + goto out; + break; + default: + av_unreachable("Invalid layout_mode"); + } if ((ret = ff_set_common_formats_from_list(ctx, packed_sample_fmts)) < 0) - return ret; + goto out; for (i = 0; i < s->nb_inputs; i++) { layouts = NULL; if ((ret = ff_add_channel_layout(&layouts, INLAYOUT(ctx, i))) < 0) - return ret; + goto out; if ((ret = ff_channel_layouts_ref(layouts, &ctx->inputs[i]->outcfg.channel_layouts)) < 0) - return ret; + goto out; } layouts = NULL; if ((ret = ff_add_channel_layout(&layouts, &outlayout)) < 0) - return ret; + goto out; if ((ret = ff_channel_layouts_ref(layouts, &ctx->outputs[0]->incfg.channel_layouts)) < 0) - return ret; + goto out; - return ff_set_common_all_samplerates(ctx); + ret = ff_set_common_all_samplerates(ctx); +out: + av_channel_layout_uninit(&outlayout); + return ret; } static int config_output(AVFilterLink *outlink) _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
