Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * Copyright (c) 2002 Anders Johansson <ajh@atri.curtin.edu.au> | ||
3 | * Copyright (c) 2011 Clément Bœsch <u pkh me> | ||
4 | * Copyright (c) 2011 Nicolas George <nicolas.george@normalesup.org> | ||
5 | * | ||
6 | * This file is part of FFmpeg. | ||
7 | * | ||
8 | * FFmpeg is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU Lesser General Public | ||
10 | * License as published by the Free Software Foundation; either | ||
11 | * version 2.1 of the License, or (at your option) any later version. | ||
12 | * | ||
13 | * FFmpeg is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU Lesser General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU Lesser General Public | ||
19 | * License along with FFmpeg; if not, write to the Free Software | ||
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
21 | */ | ||
22 | |||
23 | /** | ||
24 | * @file | ||
25 | * Audio panning filter (channels mixing) | ||
26 | * Original code written by Anders Johansson for MPlayer, | ||
27 | * reimplemented for FFmpeg. | ||
28 | */ | ||
29 | |||
30 | #include <stdio.h> | ||
31 | #include "libavutil/avstring.h" | ||
32 | #include "libavutil/channel_layout.h" | ||
33 | #include "libavutil/mem.h" | ||
34 | #include "libavutil/opt.h" | ||
35 | #include "libswresample/swresample.h" | ||
36 | #include "audio.h" | ||
37 | #include "avfilter.h" | ||
38 | #include "formats.h" | ||
39 | #include "internal.h" | ||
40 | |||
41 | #define MAX_CHANNELS 64 | ||
42 | |||
43 | typedef struct PanContext { | ||
44 | const AVClass *class; | ||
45 | char *args; | ||
46 | AVChannelLayout out_channel_layout; | ||
47 | double gain[MAX_CHANNELS][MAX_CHANNELS]; | ||
48 | int64_t need_renorm; | ||
49 | int need_renumber; | ||
50 | int nb_output_channels; | ||
51 | |||
52 | int pure_gains; | ||
53 | /* channel mapping specific */ | ||
54 | int channel_map[MAX_CHANNELS]; | ||
55 | struct SwrContext *swr; | ||
56 | } PanContext; | ||
57 | |||
58 | 588 | static void skip_spaces(char **arg) | |
59 | { | ||
60 | 588 | int len = 0; | |
61 | |||
62 | 588 | sscanf(*arg, " %n", &len); | |
63 | 588 | *arg += len; | |
64 | 588 | } | |
65 | |||
66 | 294 | static int parse_channel_name(char **arg, int *rchannel, int *rnamed) | |
67 | { | ||
68 | char buf[8]; | ||
69 | 294 | int len, channel_id = 0; | |
70 | |||
71 | 294 | skip_spaces(arg); | |
72 | /* try to parse a channel name, e.g. "FL" */ | ||
73 |
2/2✓ Branch 0 taken 36 times.
✓ Branch 1 taken 258 times.
|
294 | if (sscanf(*arg, "%7[A-Z]%n", buf, &len)) { |
74 | 36 | channel_id = av_channel_from_string(buf); | |
75 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
|
36 | if (channel_id < 0) |
76 | ✗ | return channel_id; | |
77 | |||
78 | 36 | *rchannel = channel_id; | |
79 | 36 | *rnamed = 1; | |
80 | 36 | *arg += len; | |
81 | 36 | return 0; | |
82 | } | ||
83 | /* try to parse a channel number, e.g. "c2" */ | ||
84 |
1/2✓ Branch 0 taken 258 times.
✗ Branch 1 not taken.
|
258 | if (sscanf(*arg, "c%d%n", &channel_id, &len) && |
85 |
2/4✓ Branch 0 taken 258 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 258 times.
✗ Branch 3 not taken.
|
258 | channel_id >= 0 && channel_id < MAX_CHANNELS) { |
86 | 258 | *rchannel = channel_id; | |
87 | 258 | *rnamed = 0; | |
88 | 258 | *arg += len; | |
89 | 258 | return 0; | |
90 | } | ||
91 | ✗ | return AVERROR(EINVAL); | |
92 | } | ||
93 | |||
94 | 48 | static av_cold int init(AVFilterContext *ctx) | |
95 | { | ||
96 | 48 | PanContext *const pan = ctx->priv; | |
97 | 48 | char *arg, *arg0, *tokenizer, *args = av_strdup(pan->args); | |
98 | 48 | int out_ch_id, in_ch_id, len, named, ret, sign = 1; | |
99 | 48 | int nb_in_channels[2] = { 0, 0 }; // number of unnamed and named input channels | |
100 | 48 | int used_out_ch[MAX_CHANNELS] = {0}; | |
101 | double gain; | ||
102 | |||
103 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
|
48 | if (!pan->args) { |
104 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
105 | "pan filter needs a channel layout and a set " | ||
106 | "of channel definitions as parameter\n"); | ||
107 | ✗ | return AVERROR(EINVAL); | |
108 | } | ||
109 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
|
48 | if (!args) |
110 | ✗ | return AVERROR(ENOMEM); | |
111 | 48 | arg = av_strtok(args, "|", &tokenizer); | |
112 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
|
48 | if (!arg) { |
113 | ✗ | av_log(ctx, AV_LOG_ERROR, "Channel layout not specified\n"); | |
114 | ✗ | ret = AVERROR(EINVAL); | |
115 | ✗ | goto fail; | |
116 | } | ||
117 | 48 | ret = ff_parse_channel_layout(&pan->out_channel_layout, | |
118 | &pan->nb_output_channels, arg, ctx); | ||
119 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
|
48 | if (ret < 0) |
120 | ✗ | goto fail; | |
121 | |||
122 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
|
48 | if (pan->nb_output_channels > MAX_CHANNELS) { |
123 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
124 | "af_pan supports a maximum of %d channels. " | ||
125 | "Feel free to ask for a higher limit.\n", MAX_CHANNELS); | ||
126 | ✗ | ret = AVERROR_PATCHWELCOME; | |
127 | ✗ | goto fail; | |
128 | } | ||
129 | |||
130 | /* parse channel specifications */ | ||
131 |
2/2✓ Branch 1 taken 122 times.
✓ Branch 2 taken 48 times.
|
170 | while ((arg = arg0 = av_strtok(NULL, "|", &tokenizer))) { |
132 | 122 | int used_in_ch[MAX_CHANNELS] = {0}; | |
133 | /* channel name */ | ||
134 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 122 times.
|
122 | if (parse_channel_name(&arg, &out_ch_id, &named)) { |
135 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
136 | "Expected out channel name, got \"%.8s\"\n", arg); | ||
137 | ✗ | ret = AVERROR(EINVAL); | |
138 | ✗ | goto fail; | |
139 | } | ||
140 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 108 times.
|
122 | if (named) { |
141 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
|
14 | if ((out_ch_id = av_channel_layout_index_from_channel(&pan->out_channel_layout, out_ch_id)) < 0) { |
142 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
143 | "Channel \"%.8s\" does not exist in the chosen layout\n", arg0); | ||
144 | ✗ | ret = AVERROR(EINVAL); | |
145 | ✗ | goto fail; | |
146 | } | ||
147 | } | ||
148 |
2/4✓ Branch 0 taken 122 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 122 times.
|
122 | if (out_ch_id < 0 || out_ch_id >= pan->nb_output_channels) { |
149 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
150 | "Invalid out channel name \"%.8s\"\n", arg0); | ||
151 | ✗ | ret = AVERROR(EINVAL); | |
152 | ✗ | goto fail; | |
153 | } | ||
154 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 122 times.
|
122 | if (used_out_ch[out_ch_id]) { |
155 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
156 | "Can not reference out channel %d twice\n", out_ch_id); | ||
157 | ✗ | ret = AVERROR(EINVAL); | |
158 | ✗ | goto fail; | |
159 | } | ||
160 | 122 | used_out_ch[out_ch_id] = 1; | |
161 | 122 | skip_spaces(&arg); | |
162 |
2/2✓ Branch 0 taken 114 times.
✓ Branch 1 taken 8 times.
|
122 | if (*arg == '=') { |
163 | 114 | arg++; | |
164 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | } else if (*arg == '<') { |
165 | 8 | pan->need_renorm |= (int64_t)1 << out_ch_id; | |
166 | 8 | arg++; | |
167 | } else { | ||
168 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
169 | "Syntax error after channel name in \"%.8s\"\n", arg0); | ||
170 | ✗ | ret = AVERROR(EINVAL); | |
171 | ✗ | goto fail; | |
172 | } | ||
173 | /* gains */ | ||
174 | 122 | sign = 1; | |
175 | while (1) { | ||
176 | 172 | gain = 1; | |
177 |
2/2✓ Branch 0 taken 36 times.
✓ Branch 1 taken 136 times.
|
172 | if (sscanf(arg, "%lf%n *%n", &gain, &len, &len)) |
178 | 36 | arg += len; | |
179 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 172 times.
|
172 | if (parse_channel_name(&arg, &in_ch_id, &named)){ |
180 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
181 | "Expected in channel name, got \"%.8s\"\n", arg); | ||
182 | ✗ | ret = AVERROR(EINVAL); | |
183 | ✗ | goto fail; | |
184 | } | ||
185 | 172 | nb_in_channels[named]++; | |
186 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 172 times.
|
172 | if (nb_in_channels[!named]) { |
187 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
188 | "Can not mix named and numbered channels\n"); | ||
189 | ✗ | ret = AVERROR(EINVAL); | |
190 | ✗ | goto fail; | |
191 | } | ||
192 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 172 times.
|
172 | if (used_in_ch[in_ch_id]) { |
193 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
194 | "Can not reference in channel %d twice\n", in_ch_id); | ||
195 | ✗ | ret = AVERROR(EINVAL); | |
196 | ✗ | goto fail; | |
197 | } | ||
198 | 172 | used_in_ch[in_ch_id] = 1; | |
199 | 172 | pan->gain[out_ch_id][in_ch_id] = sign * gain; | |
200 | 172 | skip_spaces(&arg); | |
201 |
2/2✓ Branch 0 taken 122 times.
✓ Branch 1 taken 50 times.
|
172 | if (!*arg) |
202 | 122 | break; | |
203 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 32 times.
|
50 | if (*arg == '-') { |
204 | 18 | sign = -1; | |
205 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
|
32 | } else if (*arg != '+') { |
206 | ✗ | av_log(ctx, AV_LOG_ERROR, "Syntax error near \"%.8s\"\n", arg); | |
207 | ✗ | ret = AVERROR(EINVAL); | |
208 | ✗ | goto fail; | |
209 | } else { | ||
210 | 32 | sign = 1; | |
211 | } | ||
212 | 50 | arg++; | |
213 | } | ||
214 | } | ||
215 | 48 | pan->need_renumber = !!nb_in_channels[1]; | |
216 | |||
217 | 48 | ret = 0; | |
218 | 48 | fail: | |
219 | 48 | av_free(args); | |
220 | 48 | return ret; | |
221 | } | ||
222 | |||
223 | 24 | static int are_gains_pure(const PanContext *pan) | |
224 | { | ||
225 | int i, j; | ||
226 | |||
227 |
2/2✓ Branch 0 taken 1032 times.
✓ Branch 1 taken 16 times.
|
1048 | for (i = 0; i < MAX_CHANNELS; i++) { |
228 | 1032 | int nb_gain = 0; | |
229 | |||
230 |
2/2✓ Branch 0 taken 65552 times.
✓ Branch 1 taken 1024 times.
|
66576 | for (j = 0; j < MAX_CHANNELS; j++) { |
231 | 65552 | double gain = pan->gain[i][j]; | |
232 | |||
233 | /* channel mapping is effective only if 0% or 100% of a channel is | ||
234 | * selected... */ | ||
235 |
4/4✓ Branch 0 taken 48 times.
✓ Branch 1 taken 65504 times.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 41 times.
|
65552 | if (gain != 0. && gain != 1.) |
236 | 7 | return 0; | |
237 | /* ...and if the output channel is only composed of one input */ | ||
238 |
4/4✓ Branch 0 taken 41 times.
✓ Branch 1 taken 65504 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 40 times.
|
65545 | if (gain && nb_gain++) |
239 | 1 | return 0; | |
240 | } | ||
241 | } | ||
242 | 16 | return 1; | |
243 | } | ||
244 | |||
245 | 24 | static int query_formats(AVFilterContext *ctx) | |
246 | { | ||
247 | 24 | PanContext *pan = ctx->priv; | |
248 | 24 | AVFilterLink *inlink = ctx->inputs[0]; | |
249 | 24 | AVFilterLink *outlink = ctx->outputs[0]; | |
250 | AVFilterChannelLayouts *layouts; | ||
251 | int ret; | ||
252 | |||
253 | 24 | pan->pure_gains = are_gains_pure(pan); | |
254 | /* libswr supports any sample and packing formats */ | ||
255 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 24 times.
|
24 | if ((ret = ff_set_common_formats(ctx, ff_all_formats(AVMEDIA_TYPE_AUDIO))) < 0) |
256 | ✗ | return ret; | |
257 | |||
258 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
|
24 | if ((ret = ff_set_common_all_samplerates(ctx)) < 0) |
259 | ✗ | return ret; | |
260 | |||
261 | // inlink supports any channel layout | ||
262 | 24 | layouts = ff_all_channel_counts(); | |
263 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
|
24 | if ((ret = ff_channel_layouts_ref(layouts, &inlink->outcfg.channel_layouts)) < 0) |
264 | ✗ | return ret; | |
265 | |||
266 | // outlink supports only requested output channel layout | ||
267 | 24 | layouts = NULL; | |
268 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
|
24 | if ((ret = ff_add_channel_layout(&layouts, &pan->out_channel_layout)) < 0) |
269 | ✗ | return ret; | |
270 | 24 | return ff_channel_layouts_ref(layouts, &outlink->incfg.channel_layouts); | |
271 | } | ||
272 | |||
273 | 24 | static int config_props(AVFilterLink *link) | |
274 | { | ||
275 | 24 | AVFilterContext *ctx = link->dst; | |
276 | 24 | PanContext *pan = ctx->priv; | |
277 | char buf[1024], *cur; | ||
278 | int i, j, k, r, ret; | ||
279 | double t; | ||
280 | |||
281 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 21 times.
|
24 | if (pan->need_renumber) { |
282 | // input channels were given by their name: renumber them | ||
283 |
2/2✓ Branch 0 taken 192 times.
✓ Branch 1 taken 3 times.
|
195 | for (i = j = 0; i < MAX_CHANNELS; i++) { |
284 |
2/2✓ Branch 1 taken 9 times.
✓ Branch 2 taken 183 times.
|
192 | if (av_channel_layout_index_from_channel(&link->ch_layout, i) >= 0) { |
285 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 9 times.
|
25 | for (k = 0; k < pan->nb_output_channels; k++) |
286 | 16 | pan->gain[k][j] = pan->gain[k][i]; | |
287 | 9 | j++; | |
288 | } | ||
289 | } | ||
290 | } | ||
291 | |||
292 | // sanity check; can't be done in query_formats since the inlink | ||
293 | // channel layout is unknown at that time | ||
294 |
1/2✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
|
24 | if (link->ch_layout.nb_channels > MAX_CHANNELS || |
295 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
|
24 | pan->nb_output_channels > MAX_CHANNELS) { |
296 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
297 | "af_pan supports a maximum of %d channels. " | ||
298 | "Feel free to ask for a higher limit.\n", MAX_CHANNELS); | ||
299 | ✗ | return AVERROR_PATCHWELCOME; | |
300 | } | ||
301 | |||
302 | // init libswresample context | ||
303 | 24 | ret = swr_alloc_set_opts2(&pan->swr, | |
304 | 24 | &pan->out_channel_layout, link->format, link->sample_rate, | |
305 | 24 | &link->ch_layout, link->format, link->sample_rate, | |
306 | 0, ctx); | ||
307 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
|
24 | if (ret < 0) |
308 | ✗ | return AVERROR(ENOMEM); | |
309 | |||
310 | // gains are pure, init the channel mapping | ||
311 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 8 times.
|
24 | if (pan->pure_gains) { |
312 | |||
313 | // get channel map from the pure gains | ||
314 |
2/2✓ Branch 0 taken 34 times.
✓ Branch 1 taken 16 times.
|
50 | for (i = 0; i < pan->nb_output_channels; i++) { |
315 | 34 | int ch_id = -1; | |
316 |
2/2✓ Branch 0 taken 35 times.
✓ Branch 1 taken 7 times.
|
42 | for (j = 0; j < link->ch_layout.nb_channels; j++) { |
317 |
2/2✓ Branch 0 taken 27 times.
✓ Branch 1 taken 8 times.
|
35 | if (pan->gain[i][j]) { |
318 | 27 | ch_id = j; | |
319 | 27 | break; | |
320 | } | ||
321 | } | ||
322 | 34 | pan->channel_map[i] = ch_id; | |
323 | } | ||
324 | |||
325 | 16 | av_opt_set_chlayout(pan->swr, "uchl", &pan->out_channel_layout, 0); | |
326 | 16 | swr_set_channel_mapping(pan->swr, pan->channel_map); | |
327 | } else { | ||
328 | // renormalize | ||
329 |
2/2✓ Branch 0 taken 27 times.
✓ Branch 1 taken 8 times.
|
35 | for (i = 0; i < pan->nb_output_channels; i++) { |
330 |
2/2✓ Branch 0 taken 23 times.
✓ Branch 1 taken 4 times.
|
27 | if (!((pan->need_renorm >> i) & 1)) |
331 | 23 | continue; | |
332 | 4 | t = 0; | |
333 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 4 times.
|
16 | for (j = 0; j < link->ch_layout.nb_channels; j++) |
334 | 12 | t += fabs(pan->gain[i][j]); | |
335 |
2/4✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
|
4 | if (t > -1E-5 && t < 1E-5) { |
336 | // t is almost 0 but not exactly, this is probably a mistake | ||
337 | ✗ | if (t) | |
338 | ✗ | av_log(ctx, AV_LOG_WARNING, | |
339 | "Degenerate coefficients while renormalizing\n"); | ||
340 | ✗ | continue; | |
341 | } | ||
342 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 4 times.
|
16 | for (j = 0; j < link->ch_layout.nb_channels; j++) |
343 | 12 | pan->gain[i][j] /= t; | |
344 | } | ||
345 | 8 | swr_set_matrix(pan->swr, pan->gain[0], pan->gain[1] - pan->gain[0]); | |
346 | } | ||
347 | |||
348 | 24 | r = swr_init(pan->swr); | |
349 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
|
24 | if (r < 0) |
350 | ✗ | return r; | |
351 | |||
352 | // summary | ||
353 |
2/2✓ Branch 0 taken 61 times.
✓ Branch 1 taken 24 times.
|
85 | for (i = 0; i < pan->nb_output_channels; i++) { |
354 | 61 | cur = buf; | |
355 |
2/2✓ Branch 0 taken 162 times.
✓ Branch 1 taken 61 times.
|
223 | for (j = 0; j < link->ch_layout.nb_channels; j++) { |
356 |
2/2✓ Branch 0 taken 101 times.
✓ Branch 1 taken 61 times.
|
162 | r = snprintf(cur, buf + sizeof(buf) - cur, "%s%.3g i%d", |
357 | j ? " + " : "", pan->gain[i][j], j); | ||
358 |
1/2✓ Branch 0 taken 162 times.
✗ Branch 1 not taken.
|
162 | cur += FFMIN(buf + sizeof(buf) - cur, r); |
359 | } | ||
360 | 61 | av_log(ctx, AV_LOG_VERBOSE, "o%d = %s\n", i, buf); | |
361 | } | ||
362 | // add channel mapping summary if possible | ||
363 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 8 times.
|
24 | if (pan->pure_gains) { |
364 | 16 | av_log(ctx, AV_LOG_INFO, "Pure channel mapping detected:"); | |
365 |
2/2✓ Branch 0 taken 34 times.
✓ Branch 1 taken 16 times.
|
50 | for (i = 0; i < pan->nb_output_channels; i++) |
366 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 27 times.
|
34 | if (pan->channel_map[i] < 0) |
367 | 7 | av_log(ctx, AV_LOG_INFO, " M"); | |
368 | else | ||
369 | 27 | av_log(ctx, AV_LOG_INFO, " %d", pan->channel_map[i]); | |
370 | 16 | av_log(ctx, AV_LOG_INFO, "\n"); | |
371 | 16 | return 0; | |
372 | } | ||
373 | 8 | return 0; | |
374 | } | ||
375 | |||
376 | 1110 | static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) | |
377 | { | ||
378 | int ret; | ||
379 | 1110 | int n = insamples->nb_samples; | |
380 | 1110 | AVFilterLink *const outlink = inlink->dst->outputs[0]; | |
381 | 1110 | AVFrame *outsamples = ff_get_audio_buffer(outlink, n); | |
382 | 1110 | PanContext *pan = inlink->dst->priv; | |
383 | |||
384 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1110 times.
|
1110 | if (!outsamples) { |
385 | ✗ | av_frame_free(&insamples); | |
386 | ✗ | return AVERROR(ENOMEM); | |
387 | } | ||
388 | 1110 | swr_convert(pan->swr, outsamples->extended_data, n, | |
389 | 1110 | (void *)insamples->extended_data, n); | |
390 | 1110 | av_frame_copy_props(outsamples, insamples); | |
391 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1110 times.
|
1110 | if ((ret = av_channel_layout_copy(&outsamples->ch_layout, &outlink->ch_layout)) < 0) { |
392 | ✗ | av_frame_free(&outsamples); | |
393 | ✗ | av_frame_free(&insamples); | |
394 | ✗ | return ret; | |
395 | } | ||
396 | |||
397 | 1110 | av_frame_free(&insamples); | |
398 | 1110 | return ff_filter_frame(outlink, outsamples); | |
399 | } | ||
400 | |||
401 | 48 | static av_cold void uninit(AVFilterContext *ctx) | |
402 | { | ||
403 | 48 | PanContext *pan = ctx->priv; | |
404 | 48 | swr_free(&pan->swr); | |
405 | 48 | av_channel_layout_uninit(&pan->out_channel_layout); | |
406 | 48 | } | |
407 | |||
408 | #define OFFSET(x) offsetof(PanContext, x) | ||
409 | |||
410 | static const AVOption pan_options[] = { | ||
411 | { "args", NULL, OFFSET(args), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM }, | ||
412 | { NULL } | ||
413 | }; | ||
414 | |||
415 | AVFILTER_DEFINE_CLASS(pan); | ||
416 | |||
417 | static const AVFilterPad pan_inputs[] = { | ||
418 | { | ||
419 | .name = "default", | ||
420 | .type = AVMEDIA_TYPE_AUDIO, | ||
421 | .config_props = config_props, | ||
422 | .filter_frame = filter_frame, | ||
423 | }, | ||
424 | }; | ||
425 | |||
426 | const AVFilter ff_af_pan = { | ||
427 | .name = "pan", | ||
428 | .description = NULL_IF_CONFIG_SMALL("Remix channels with coefficients (panning)."), | ||
429 | .priv_size = sizeof(PanContext), | ||
430 | .priv_class = &pan_class, | ||
431 | .init = init, | ||
432 | .uninit = uninit, | ||
433 | FILTER_INPUTS(pan_inputs), | ||
434 | FILTER_OUTPUTS(ff_audio_default_filterpad), | ||
435 | FILTER_QUERY_FUNC(query_formats), | ||
436 | }; | ||
437 |