mirror of
https://git.ffmpeg.org/ffmpeg.git
synced 2025-12-22 06:50:12 +01:00
avfilter/af_amerge: add layout_mode option to control output channel layout
Signed-off-by: Marton Balint <cus@passwd.hu>
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user