Commit 4e1307c0 authored by Paul B Mahol's avatar Paul B Mahol

avfilter/af_channelsplit: add channels option

So user can pick which channels to extract.
Signed-off-by: 's avatarPaul B Mahol <onemda@gmail.com>
parent abf35afb
...@@ -2208,8 +2208,17 @@ It accepts the following parameters: ...@@ -2208,8 +2208,17 @@ It accepts the following parameters:
@table @option @table @option
@item channel_layout @item channel_layout
The channel layout of the input stream. The default is "stereo". The channel layout of the input stream. The default is "stereo".
@item channels
A channel layout describing the channels to be extracted as separate output streams
or "all" to extract each input channel as a separate stream. The default is "all".
Choosing channels not present in channel layout in the input will result in an error.
@end table @end table
@subsection Examples
@itemize
@item
For example, assuming a stereo input MP3 file, For example, assuming a stereo input MP3 file,
@example @example
ffmpeg -i in.mp3 -filter_complex channelsplit out.mkv ffmpeg -i in.mp3 -filter_complex channelsplit out.mkv
...@@ -2217,6 +2226,7 @@ ffmpeg -i in.mp3 -filter_complex channelsplit out.mkv ...@@ -2217,6 +2226,7 @@ ffmpeg -i in.mp3 -filter_complex channelsplit out.mkv
will create an output Matroska file with two audio streams, one containing only will create an output Matroska file with two audio streams, one containing only
the left channel and the other the right channel. the left channel and the other the right channel.
@item
Split a 5.1 WAV file into per-channel files: Split a 5.1 WAV file into per-channel files:
@example @example
ffmpeg -i in.wav -filter_complex ffmpeg -i in.wav -filter_complex
...@@ -2226,6 +2236,14 @@ front_center.wav -map '[LFE]' lfe.wav -map '[SL]' side_left.wav -map '[SR]' ...@@ -2226,6 +2236,14 @@ front_center.wav -map '[LFE]' lfe.wav -map '[SL]' side_left.wav -map '[SR]'
side_right.wav side_right.wav
@end example @end example
@item
Extract only LFE from a 5.1 WAV file:
@example
ffmpeg -i in.wav -filter_complex 'channelsplit=channel_layout=5.1:channels=LFE[LFE]'
-map '[LFE]' lfe.wav
@end example
@end itemize
@section chorus @section chorus
Add a chorus effect to the audio. Add a chorus effect to the audio.
......
...@@ -38,6 +38,9 @@ typedef struct ChannelSplitContext { ...@@ -38,6 +38,9 @@ typedef struct ChannelSplitContext {
uint64_t channel_layout; uint64_t channel_layout;
char *channel_layout_str; char *channel_layout_str;
char *channels_str;
int map[64];
} ChannelSplitContext; } ChannelSplitContext;
#define OFFSET(x) offsetof(ChannelSplitContext, x) #define OFFSET(x) offsetof(ChannelSplitContext, x)
...@@ -45,6 +48,7 @@ typedef struct ChannelSplitContext { ...@@ -45,6 +48,7 @@ typedef struct ChannelSplitContext {
#define F AV_OPT_FLAG_FILTERING_PARAM #define F AV_OPT_FLAG_FILTERING_PARAM
static const AVOption channelsplit_options[] = { static const AVOption channelsplit_options[] = {
{ "channel_layout", "Input channel layout.", OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, { .str = "stereo" }, .flags = A|F }, { "channel_layout", "Input channel layout.", OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, { .str = "stereo" }, .flags = A|F },
{ "channels", "Channels to extract.", OFFSET(channels_str), AV_OPT_TYPE_STRING, { .str = "all" }, .flags = A|F },
{ NULL } { NULL }
}; };
...@@ -53,8 +57,9 @@ AVFILTER_DEFINE_CLASS(channelsplit); ...@@ -53,8 +57,9 @@ AVFILTER_DEFINE_CLASS(channelsplit);
static av_cold int init(AVFilterContext *ctx) static av_cold int init(AVFilterContext *ctx)
{ {
ChannelSplitContext *s = ctx->priv; ChannelSplitContext *s = ctx->priv;
uint64_t channel_layout;
int nb_channels; int nb_channels;
int ret = 0, i; int all = 0, ret = 0, i;
if (!(s->channel_layout = av_get_channel_layout(s->channel_layout_str))) { if (!(s->channel_layout = av_get_channel_layout(s->channel_layout_str))) {
av_log(ctx, AV_LOG_ERROR, "Error parsing channel layout '%s'.\n", av_log(ctx, AV_LOG_ERROR, "Error parsing channel layout '%s'.\n",
...@@ -63,14 +68,35 @@ static av_cold int init(AVFilterContext *ctx) ...@@ -63,14 +68,35 @@ static av_cold int init(AVFilterContext *ctx)
goto fail; goto fail;
} }
nb_channels = av_get_channel_layout_nb_channels(s->channel_layout);
if (!strcmp(s->channels_str, "all")) {
nb_channels = av_get_channel_layout_nb_channels(s->channel_layout);
channel_layout = s->channel_layout;
all = 1;
} else {
if ((ret = av_get_extended_channel_layout(s->channels_str, &channel_layout, &nb_channels)) < 0)
return ret;
}
for (i = 0; i < nb_channels; i++) { for (i = 0; i < nb_channels; i++) {
uint64_t channel = av_channel_layout_extract_channel(s->channel_layout, i); uint64_t channel = av_channel_layout_extract_channel(channel_layout, i);
AVFilterPad pad = { 0 }; AVFilterPad pad = { 0 };
pad.type = AVMEDIA_TYPE_AUDIO; pad.type = AVMEDIA_TYPE_AUDIO;
pad.name = av_get_channel_name(channel); pad.name = av_get_channel_name(channel);
if (all) {
s->map[i] = i;
} else {
if ((ret = av_get_channel_layout_channel_index(s->channel_layout, channel)) < 0) {
av_log(ctx, AV_LOG_ERROR, "Channel name '%s' not present in channel layout '%s'.\n",
av_get_channel_name(channel), s->channel_layout_str);
return ret;
}
s->map[i] = ret;
}
if ((ret = ff_insert_outpad(ctx, i, &pad)) < 0) { if ((ret = ff_insert_outpad(ctx, i, &pad)) < 0) {
return ret; return ret;
} }
...@@ -96,7 +122,7 @@ static int query_formats(AVFilterContext *ctx) ...@@ -96,7 +122,7 @@ static int query_formats(AVFilterContext *ctx)
for (i = 0; i < ctx->nb_outputs; i++) { for (i = 0; i < ctx->nb_outputs; i++) {
AVFilterChannelLayouts *out_layouts = NULL; AVFilterChannelLayouts *out_layouts = NULL;
uint64_t channel = av_channel_layout_extract_channel(s->channel_layout, i); uint64_t channel = av_channel_layout_extract_channel(s->channel_layout, s->map[i]);
if ((ret = ff_add_channel_layout(&out_layouts, channel)) < 0 || if ((ret = ff_add_channel_layout(&out_layouts, channel)) < 0 ||
(ret = ff_channel_layouts_ref(out_layouts, &ctx->outputs[i]->in_channel_layouts)) < 0) (ret = ff_channel_layouts_ref(out_layouts, &ctx->outputs[i]->in_channel_layouts)) < 0)
...@@ -109,6 +135,7 @@ static int query_formats(AVFilterContext *ctx) ...@@ -109,6 +135,7 @@ static int query_formats(AVFilterContext *ctx)
static int filter_frame(AVFilterLink *inlink, AVFrame *buf) static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
{ {
AVFilterContext *ctx = inlink->dst; AVFilterContext *ctx = inlink->dst;
ChannelSplitContext *s = ctx->priv;
int i, ret = 0; int i, ret = 0;
for (i = 0; i < ctx->nb_outputs; i++) { for (i = 0; i < ctx->nb_outputs; i++) {
...@@ -119,9 +146,9 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf) ...@@ -119,9 +146,9 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
break; break;
} }
buf_out->data[0] = buf_out->extended_data[0] = buf_out->extended_data[i]; buf_out->data[0] = buf_out->extended_data[0] = buf_out->extended_data[s->map[i]];
buf_out->channel_layout = buf_out->channel_layout =
av_channel_layout_extract_channel(buf->channel_layout, i); av_channel_layout_extract_channel(buf->channel_layout, s->map[i]);
buf_out->channels = 1; buf_out->channels = 1;
ret = ff_filter_frame(ctx->outputs[i], buf_out); ret = ff_filter_frame(ctx->outputs[i], buf_out);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment