1 |
|
|
/* |
2 |
|
|
* This file is part of FFmpeg. |
3 |
|
|
* |
4 |
|
|
* FFmpeg is free software; you can redistribute it and/or |
5 |
|
|
* modify it under the terms of the GNU Lesser General Public |
6 |
|
|
* License as published by the Free Software Foundation; either |
7 |
|
|
* version 2.1 of the License, or (at your option) any later version. |
8 |
|
|
* |
9 |
|
|
* FFmpeg is distributed in the hope that it will be useful, |
10 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 |
|
|
* Lesser General Public License for more details. |
13 |
|
|
* |
14 |
|
|
* You should have received a copy of the GNU Lesser General Public |
15 |
|
|
* License along with FFmpeg; if not, write to the Free Software |
16 |
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
17 |
|
|
*/ |
18 |
|
|
|
19 |
|
|
/** |
20 |
|
|
* @file |
21 |
|
|
* Channel split filter |
22 |
|
|
* |
23 |
|
|
* Split an audio stream into per-channel streams. |
24 |
|
|
*/ |
25 |
|
|
|
26 |
|
|
#include "libavutil/attributes.h" |
27 |
|
|
#include "libavutil/channel_layout.h" |
28 |
|
|
#include "libavutil/internal.h" |
29 |
|
|
#include "libavutil/opt.h" |
30 |
|
|
|
31 |
|
|
#include "audio.h" |
32 |
|
|
#include "avfilter.h" |
33 |
|
|
#include "formats.h" |
34 |
|
|
#include "internal.h" |
35 |
|
|
|
36 |
|
|
typedef struct ChannelSplitContext { |
37 |
|
|
const AVClass *class; |
38 |
|
|
|
39 |
|
|
uint64_t channel_layout; |
40 |
|
|
char *channel_layout_str; |
41 |
|
|
char *channels_str; |
42 |
|
|
|
43 |
|
|
int map[64]; |
44 |
|
|
} ChannelSplitContext; |
45 |
|
|
|
46 |
|
|
#define OFFSET(x) offsetof(ChannelSplitContext, x) |
47 |
|
|
#define A AV_OPT_FLAG_AUDIO_PARAM |
48 |
|
|
#define F AV_OPT_FLAG_FILTERING_PARAM |
49 |
|
|
static const AVOption channelsplit_options[] = { |
50 |
|
|
{ "channel_layout", "Input channel layout.", OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, { .str = "stereo" }, .flags = A|F }, |
51 |
|
|
{ "channels", "Channels to extract.", OFFSET(channels_str), AV_OPT_TYPE_STRING, { .str = "all" }, .flags = A|F }, |
52 |
|
|
{ NULL } |
53 |
|
|
}; |
54 |
|
|
|
55 |
|
|
AVFILTER_DEFINE_CLASS(channelsplit); |
56 |
|
|
|
57 |
|
2 |
static av_cold int init(AVFilterContext *ctx) |
58 |
|
|
{ |
59 |
|
2 |
ChannelSplitContext *s = ctx->priv; |
60 |
|
|
uint64_t channel_layout; |
61 |
|
|
int nb_channels; |
62 |
|
2 |
int all = 0, ret = 0, i; |
63 |
|
|
|
64 |
✗✓ |
2 |
if (!(s->channel_layout = av_get_channel_layout(s->channel_layout_str))) { |
65 |
|
|
av_log(ctx, AV_LOG_ERROR, "Error parsing channel layout '%s'.\n", |
66 |
|
|
s->channel_layout_str); |
67 |
|
|
ret = AVERROR(EINVAL); |
68 |
|
|
goto fail; |
69 |
|
|
} |
70 |
|
|
|
71 |
|
|
|
72 |
✓✗ |
2 |
if (!strcmp(s->channels_str, "all")) { |
73 |
|
2 |
nb_channels = av_get_channel_layout_nb_channels(s->channel_layout); |
74 |
|
2 |
channel_layout = s->channel_layout; |
75 |
|
2 |
all = 1; |
76 |
|
|
} else { |
77 |
|
|
if ((ret = av_get_extended_channel_layout(s->channels_str, &channel_layout, &nb_channels)) < 0) |
78 |
|
|
return ret; |
79 |
|
|
} |
80 |
|
|
|
81 |
✓✓ |
6 |
for (i = 0; i < nb_channels; i++) { |
82 |
|
4 |
uint64_t channel = av_channel_layout_extract_channel(channel_layout, i); |
83 |
|
4 |
AVFilterPad pad = { 0 }; |
84 |
|
|
|
85 |
|
4 |
pad.type = AVMEDIA_TYPE_AUDIO; |
86 |
|
4 |
pad.name = av_get_channel_name(channel); |
87 |
|
|
|
88 |
✓✗ |
4 |
if (all) { |
89 |
|
4 |
s->map[i] = i; |
90 |
|
|
} else { |
91 |
|
|
if ((ret = av_get_channel_layout_channel_index(s->channel_layout, channel)) < 0) { |
92 |
|
|
av_log(ctx, AV_LOG_ERROR, "Channel name '%s' not present in channel layout '%s'.\n", |
93 |
|
|
av_get_channel_name(channel), s->channel_layout_str); |
94 |
|
|
return ret; |
95 |
|
|
} |
96 |
|
|
|
97 |
|
|
s->map[i] = ret; |
98 |
|
|
} |
99 |
|
|
|
100 |
✗✓ |
4 |
if ((ret = ff_insert_outpad(ctx, i, &pad)) < 0) { |
101 |
|
|
return ret; |
102 |
|
|
} |
103 |
|
|
} |
104 |
|
|
|
105 |
|
2 |
fail: |
106 |
|
2 |
return ret; |
107 |
|
|
} |
108 |
|
|
|
109 |
|
1 |
static int query_formats(AVFilterContext *ctx) |
110 |
|
|
{ |
111 |
|
1 |
ChannelSplitContext *s = ctx->priv; |
112 |
|
1 |
AVFilterChannelLayouts *in_layouts = NULL; |
113 |
|
|
int i, ret; |
114 |
|
|
|
115 |
✓✗✗✓
|
2 |
if ((ret = ff_set_common_formats(ctx, ff_planar_sample_fmts())) < 0 || |
116 |
|
1 |
(ret = ff_set_common_samplerates(ctx, ff_all_samplerates())) < 0) |
117 |
|
|
return ret; |
118 |
|
|
|
119 |
✓✗✗✓
|
2 |
if ((ret = ff_add_channel_layout(&in_layouts, s->channel_layout)) < 0 || |
120 |
|
1 |
(ret = ff_channel_layouts_ref(in_layouts, &ctx->inputs[0]->outcfg.channel_layouts)) < 0) |
121 |
|
|
return ret; |
122 |
|
|
|
123 |
✓✓ |
3 |
for (i = 0; i < ctx->nb_outputs; i++) { |
124 |
|
2 |
AVFilterChannelLayouts *out_layouts = NULL; |
125 |
|
2 |
uint64_t channel = av_channel_layout_extract_channel(s->channel_layout, s->map[i]); |
126 |
|
|
|
127 |
✓✗✗✓
|
4 |
if ((ret = ff_add_channel_layout(&out_layouts, channel)) < 0 || |
128 |
|
2 |
(ret = ff_channel_layouts_ref(out_layouts, &ctx->outputs[i]->incfg.channel_layouts)) < 0) |
129 |
|
|
return ret; |
130 |
|
|
} |
131 |
|
|
|
132 |
|
1 |
return 0; |
133 |
|
|
} |
134 |
|
|
|
135 |
|
259 |
static int filter_frame(AVFilterLink *inlink, AVFrame *buf) |
136 |
|
|
{ |
137 |
|
259 |
AVFilterContext *ctx = inlink->dst; |
138 |
|
259 |
ChannelSplitContext *s = ctx->priv; |
139 |
|
259 |
int i, ret = 0; |
140 |
|
|
|
141 |
✓✓ |
777 |
for (i = 0; i < ctx->nb_outputs; i++) { |
142 |
|
518 |
AVFrame *buf_out = av_frame_clone(buf); |
143 |
|
|
|
144 |
✗✓ |
518 |
if (!buf_out) { |
145 |
|
|
ret = AVERROR(ENOMEM); |
146 |
|
|
break; |
147 |
|
|
} |
148 |
|
|
|
149 |
|
518 |
buf_out->data[0] = buf_out->extended_data[0] = buf_out->extended_data[s->map[i]]; |
150 |
|
518 |
buf_out->channel_layout = |
151 |
|
518 |
av_channel_layout_extract_channel(buf->channel_layout, s->map[i]); |
152 |
|
518 |
buf_out->channels = 1; |
153 |
|
|
|
154 |
|
518 |
ret = ff_filter_frame(ctx->outputs[i], buf_out); |
155 |
✗✓ |
518 |
if (ret < 0) |
156 |
|
|
break; |
157 |
|
|
} |
158 |
|
259 |
av_frame_free(&buf); |
159 |
|
259 |
return ret; |
160 |
|
|
} |
161 |
|
|
|
162 |
|
|
static const AVFilterPad avfilter_af_channelsplit_inputs[] = { |
163 |
|
|
{ |
164 |
|
|
.name = "default", |
165 |
|
|
.type = AVMEDIA_TYPE_AUDIO, |
166 |
|
|
.filter_frame = filter_frame, |
167 |
|
|
}, |
168 |
|
|
{ NULL } |
169 |
|
|
}; |
170 |
|
|
|
171 |
|
|
AVFilter ff_af_channelsplit = { |
172 |
|
|
.name = "channelsplit", |
173 |
|
|
.description = NULL_IF_CONFIG_SMALL("Split audio into per-channel streams."), |
174 |
|
|
.priv_size = sizeof(ChannelSplitContext), |
175 |
|
|
.priv_class = &channelsplit_class, |
176 |
|
|
.init = init, |
177 |
|
|
.query_formats = query_formats, |
178 |
|
|
.inputs = avfilter_af_channelsplit_inputs, |
179 |
|
|
.outputs = NULL, |
180 |
|
|
.flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS, |
181 |
|
|
}; |