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 | /* parse channel specifications */ | ||
123 |
2/2✓ Branch 1 taken 122 times.
✓ Branch 2 taken 48 times.
|
170 | while ((arg = arg0 = av_strtok(NULL, "|", &tokenizer))) { |
124 | 122 | int used_in_ch[MAX_CHANNELS] = {0}; | |
125 | /* channel name */ | ||
126 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 122 times.
|
122 | if (parse_channel_name(&arg, &out_ch_id, &named)) { |
127 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
128 | "Expected out channel name, got \"%.8s\"\n", arg); | ||
129 | ✗ | ret = AVERROR(EINVAL); | |
130 | ✗ | goto fail; | |
131 | } | ||
132 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 108 times.
|
122 | if (named) { |
133 |
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) { |
134 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
135 | "Channel \"%.8s\" does not exist in the chosen layout\n", arg0); | ||
136 | ✗ | ret = AVERROR(EINVAL); | |
137 | ✗ | goto fail; | |
138 | } | ||
139 | } | ||
140 |
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) { |
141 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
142 | "Invalid out channel name \"%.8s\"\n", arg0); | ||
143 | ✗ | ret = AVERROR(EINVAL); | |
144 | ✗ | goto fail; | |
145 | } | ||
146 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 122 times.
|
122 | if (used_out_ch[out_ch_id]) { |
147 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
148 | "Can not reference out channel %d twice\n", out_ch_id); | ||
149 | ✗ | ret = AVERROR(EINVAL); | |
150 | ✗ | goto fail; | |
151 | } | ||
152 | 122 | used_out_ch[out_ch_id] = 1; | |
153 | 122 | skip_spaces(&arg); | |
154 |
2/2✓ Branch 0 taken 114 times.
✓ Branch 1 taken 8 times.
|
122 | if (*arg == '=') { |
155 | 114 | arg++; | |
156 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | } else if (*arg == '<') { |
157 | 8 | pan->need_renorm |= (int64_t)1 << out_ch_id; | |
158 | 8 | arg++; | |
159 | } else { | ||
160 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
161 | "Syntax error after channel name in \"%.8s\"\n", arg0); | ||
162 | ✗ | ret = AVERROR(EINVAL); | |
163 | ✗ | goto fail; | |
164 | } | ||
165 | /* gains */ | ||
166 | 122 | sign = 1; | |
167 | while (1) { | ||
168 | 172 | gain = 1; | |
169 |
2/2✓ Branch 0 taken 36 times.
✓ Branch 1 taken 136 times.
|
172 | if (sscanf(arg, "%lf%n *%n", &gain, &len, &len)) |
170 | 36 | arg += len; | |
171 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 172 times.
|
172 | if (parse_channel_name(&arg, &in_ch_id, &named)){ |
172 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
173 | "Expected in channel name, got \"%.8s\"\n", arg); | ||
174 | ✗ | ret = AVERROR(EINVAL); | |
175 | ✗ | goto fail; | |
176 | } | ||
177 | 172 | nb_in_channels[named]++; | |
178 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 172 times.
|
172 | if (nb_in_channels[!named]) { |
179 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
180 | "Can not mix named and numbered channels\n"); | ||
181 | ✗ | ret = AVERROR(EINVAL); | |
182 | ✗ | goto fail; | |
183 | } | ||
184 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 172 times.
|
172 | if (used_in_ch[in_ch_id]) { |
185 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
186 | "Can not reference in channel %d twice\n", in_ch_id); | ||
187 | ✗ | ret = AVERROR(EINVAL); | |
188 | ✗ | goto fail; | |
189 | } | ||
190 | 172 | used_in_ch[in_ch_id] = 1; | |
191 | 172 | pan->gain[out_ch_id][in_ch_id] = sign * gain; | |
192 | 172 | skip_spaces(&arg); | |
193 |
2/2✓ Branch 0 taken 122 times.
✓ Branch 1 taken 50 times.
|
172 | if (!*arg) |
194 | 122 | break; | |
195 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 32 times.
|
50 | if (*arg == '-') { |
196 | 18 | sign = -1; | |
197 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
|
32 | } else if (*arg != '+') { |
198 | ✗ | av_log(ctx, AV_LOG_ERROR, "Syntax error near \"%.8s\"\n", arg); | |
199 | ✗ | ret = AVERROR(EINVAL); | |
200 | ✗ | goto fail; | |
201 | } else { | ||
202 | 32 | sign = 1; | |
203 | } | ||
204 | 50 | arg++; | |
205 | } | ||
206 | } | ||
207 | 48 | pan->need_renumber = !!nb_in_channels[1]; | |
208 | |||
209 | 48 | ret = 0; | |
210 | 48 | fail: | |
211 | 48 | av_free(args); | |
212 | 48 | return ret; | |
213 | } | ||
214 | |||
215 | 24 | static int are_gains_pure(const PanContext *pan) | |
216 | { | ||
217 | int i, j; | ||
218 | |||
219 |
2/2✓ Branch 0 taken 1032 times.
✓ Branch 1 taken 16 times.
|
1048 | for (i = 0; i < MAX_CHANNELS; i++) { |
220 | 1032 | int nb_gain = 0; | |
221 | |||
222 |
2/2✓ Branch 0 taken 65552 times.
✓ Branch 1 taken 1024 times.
|
66576 | for (j = 0; j < MAX_CHANNELS; j++) { |
223 | 65552 | double gain = pan->gain[i][j]; | |
224 | |||
225 | /* channel mapping is effective only if 0% or 100% of a channel is | ||
226 | * selected... */ | ||
227 |
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.) |
228 | 7 | return 0; | |
229 | /* ...and if the output channel is only composed of one input */ | ||
230 |
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++) |
231 | 1 | return 0; | |
232 | } | ||
233 | } | ||
234 | 16 | return 1; | |
235 | } | ||
236 | |||
237 | 24 | static int query_formats(AVFilterContext *ctx) | |
238 | { | ||
239 | 24 | PanContext *pan = ctx->priv; | |
240 | 24 | AVFilterLink *inlink = ctx->inputs[0]; | |
241 | 24 | AVFilterLink *outlink = ctx->outputs[0]; | |
242 | AVFilterChannelLayouts *layouts; | ||
243 | int ret; | ||
244 | |||
245 | 24 | pan->pure_gains = are_gains_pure(pan); | |
246 | /* libswr supports any sample and packing formats */ | ||
247 |
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) |
248 | ✗ | return ret; | |
249 | |||
250 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
|
24 | if ((ret = ff_set_common_all_samplerates(ctx)) < 0) |
251 | ✗ | return ret; | |
252 | |||
253 | // inlink supports any channel layout | ||
254 | 24 | layouts = ff_all_channel_counts(); | |
255 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
|
24 | if ((ret = ff_channel_layouts_ref(layouts, &inlink->outcfg.channel_layouts)) < 0) |
256 | ✗ | return ret; | |
257 | |||
258 | // outlink supports only requested output channel layout | ||
259 | 24 | layouts = NULL; | |
260 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
|
24 | if ((ret = ff_add_channel_layout(&layouts, &pan->out_channel_layout)) < 0) |
261 | ✗ | return ret; | |
262 | 24 | return ff_channel_layouts_ref(layouts, &outlink->incfg.channel_layouts); | |
263 | } | ||
264 | |||
265 | 24 | static int config_props(AVFilterLink *link) | |
266 | { | ||
267 | 24 | AVFilterContext *ctx = link->dst; | |
268 | 24 | PanContext *pan = ctx->priv; | |
269 | char buf[1024], *cur; | ||
270 | int i, j, k, r, ret; | ||
271 | double t; | ||
272 | |||
273 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 21 times.
|
24 | if (pan->need_renumber) { |
274 | // input channels were given by their name: renumber them | ||
275 |
2/2✓ Branch 0 taken 192 times.
✓ Branch 1 taken 3 times.
|
195 | for (i = j = 0; i < MAX_CHANNELS; i++) { |
276 |
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) { |
277 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 9 times.
|
25 | for (k = 0; k < pan->nb_output_channels; k++) |
278 | 16 | pan->gain[k][j] = pan->gain[k][i]; | |
279 | 9 | j++; | |
280 | } | ||
281 | } | ||
282 | } | ||
283 | |||
284 | // sanity check; can't be done in query_formats since the inlink | ||
285 | // channel layout is unknown at that time | ||
286 |
1/2✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
|
24 | if (link->ch_layout.nb_channels > MAX_CHANNELS || |
287 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
|
24 | pan->nb_output_channels > MAX_CHANNELS) { |
288 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
289 | "af_pan supports a maximum of %d channels. " | ||
290 | "Feel free to ask for a higher limit.\n", MAX_CHANNELS); | ||
291 | ✗ | return AVERROR_PATCHWELCOME; | |
292 | } | ||
293 | |||
294 | // init libswresample context | ||
295 | 24 | ret = swr_alloc_set_opts2(&pan->swr, | |
296 | 24 | &pan->out_channel_layout, link->format, link->sample_rate, | |
297 | 24 | &link->ch_layout, link->format, link->sample_rate, | |
298 | 0, ctx); | ||
299 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
|
24 | if (ret < 0) |
300 | ✗ | return AVERROR(ENOMEM); | |
301 | |||
302 | // gains are pure, init the channel mapping | ||
303 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 8 times.
|
24 | if (pan->pure_gains) { |
304 | |||
305 | // get channel map from the pure gains | ||
306 |
2/2✓ Branch 0 taken 34 times.
✓ Branch 1 taken 16 times.
|
50 | for (i = 0; i < pan->nb_output_channels; i++) { |
307 | 34 | int ch_id = -1; | |
308 |
2/2✓ Branch 0 taken 35 times.
✓ Branch 1 taken 7 times.
|
42 | for (j = 0; j < link->ch_layout.nb_channels; j++) { |
309 |
2/2✓ Branch 0 taken 27 times.
✓ Branch 1 taken 8 times.
|
35 | if (pan->gain[i][j]) { |
310 | 27 | ch_id = j; | |
311 | 27 | break; | |
312 | } | ||
313 | } | ||
314 | 34 | pan->channel_map[i] = ch_id; | |
315 | } | ||
316 | |||
317 | 16 | av_opt_set_chlayout(pan->swr, "uchl", &pan->out_channel_layout, 0); | |
318 | 16 | swr_set_channel_mapping(pan->swr, pan->channel_map); | |
319 | } else { | ||
320 | // renormalize | ||
321 |
2/2✓ Branch 0 taken 27 times.
✓ Branch 1 taken 8 times.
|
35 | for (i = 0; i < pan->nb_output_channels; i++) { |
322 |
2/2✓ Branch 0 taken 23 times.
✓ Branch 1 taken 4 times.
|
27 | if (!((pan->need_renorm >> i) & 1)) |
323 | 23 | continue; | |
324 | 4 | t = 0; | |
325 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 4 times.
|
16 | for (j = 0; j < link->ch_layout.nb_channels; j++) |
326 | 12 | t += fabs(pan->gain[i][j]); | |
327 |
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) { |
328 | // t is almost 0 but not exactly, this is probably a mistake | ||
329 | ✗ | if (t) | |
330 | ✗ | av_log(ctx, AV_LOG_WARNING, | |
331 | "Degenerate coefficients while renormalizing\n"); | ||
332 | ✗ | continue; | |
333 | } | ||
334 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 4 times.
|
16 | for (j = 0; j < link->ch_layout.nb_channels; j++) |
335 | 12 | pan->gain[i][j] /= t; | |
336 | } | ||
337 | 8 | swr_set_matrix(pan->swr, pan->gain[0], pan->gain[1] - pan->gain[0]); | |
338 | } | ||
339 | |||
340 | 24 | r = swr_init(pan->swr); | |
341 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
|
24 | if (r < 0) |
342 | ✗ | return r; | |
343 | |||
344 | // summary | ||
345 |
2/2✓ Branch 0 taken 61 times.
✓ Branch 1 taken 24 times.
|
85 | for (i = 0; i < pan->nb_output_channels; i++) { |
346 | 61 | cur = buf; | |
347 |
2/2✓ Branch 0 taken 162 times.
✓ Branch 1 taken 61 times.
|
223 | for (j = 0; j < link->ch_layout.nb_channels; j++) { |
348 |
2/2✓ Branch 0 taken 101 times.
✓ Branch 1 taken 61 times.
|
162 | r = snprintf(cur, buf + sizeof(buf) - cur, "%s%.3g i%d", |
349 | j ? " + " : "", pan->gain[i][j], j); | ||
350 |
1/2✓ Branch 0 taken 162 times.
✗ Branch 1 not taken.
|
162 | cur += FFMIN(buf + sizeof(buf) - cur, r); |
351 | } | ||
352 | 61 | av_log(ctx, AV_LOG_VERBOSE, "o%d = %s\n", i, buf); | |
353 | } | ||
354 | // add channel mapping summary if possible | ||
355 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 8 times.
|
24 | if (pan->pure_gains) { |
356 | 16 | av_log(ctx, AV_LOG_INFO, "Pure channel mapping detected:"); | |
357 |
2/2✓ Branch 0 taken 34 times.
✓ Branch 1 taken 16 times.
|
50 | for (i = 0; i < pan->nb_output_channels; i++) |
358 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 27 times.
|
34 | if (pan->channel_map[i] < 0) |
359 | 7 | av_log(ctx, AV_LOG_INFO, " M"); | |
360 | else | ||
361 | 27 | av_log(ctx, AV_LOG_INFO, " %d", pan->channel_map[i]); | |
362 | 16 | av_log(ctx, AV_LOG_INFO, "\n"); | |
363 | 16 | return 0; | |
364 | } | ||
365 | 8 | return 0; | |
366 | } | ||
367 | |||
368 | 1110 | static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) | |
369 | { | ||
370 | int ret; | ||
371 | 1110 | int n = insamples->nb_samples; | |
372 | 1110 | AVFilterLink *const outlink = inlink->dst->outputs[0]; | |
373 | 1110 | AVFrame *outsamples = ff_get_audio_buffer(outlink, n); | |
374 | 1110 | PanContext *pan = inlink->dst->priv; | |
375 | |||
376 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1110 times.
|
1110 | if (!outsamples) { |
377 | ✗ | av_frame_free(&insamples); | |
378 | ✗ | return AVERROR(ENOMEM); | |
379 | } | ||
380 | 1110 | swr_convert(pan->swr, outsamples->extended_data, n, | |
381 | 1110 | (void *)insamples->extended_data, n); | |
382 | 1110 | av_frame_copy_props(outsamples, insamples); | |
383 |
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) { |
384 | ✗ | av_frame_free(&outsamples); | |
385 | ✗ | av_frame_free(&insamples); | |
386 | ✗ | return ret; | |
387 | } | ||
388 | |||
389 | 1110 | av_frame_free(&insamples); | |
390 | 1110 | return ff_filter_frame(outlink, outsamples); | |
391 | } | ||
392 | |||
393 | 48 | static av_cold void uninit(AVFilterContext *ctx) | |
394 | { | ||
395 | 48 | PanContext *pan = ctx->priv; | |
396 | 48 | swr_free(&pan->swr); | |
397 | 48 | av_channel_layout_uninit(&pan->out_channel_layout); | |
398 | 48 | } | |
399 | |||
400 | #define OFFSET(x) offsetof(PanContext, x) | ||
401 | |||
402 | static const AVOption pan_options[] = { | ||
403 | { "args", NULL, OFFSET(args), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM }, | ||
404 | { NULL } | ||
405 | }; | ||
406 | |||
407 | AVFILTER_DEFINE_CLASS(pan); | ||
408 | |||
409 | static const AVFilterPad pan_inputs[] = { | ||
410 | { | ||
411 | .name = "default", | ||
412 | .type = AVMEDIA_TYPE_AUDIO, | ||
413 | .config_props = config_props, | ||
414 | .filter_frame = filter_frame, | ||
415 | }, | ||
416 | }; | ||
417 | |||
418 | const AVFilter ff_af_pan = { | ||
419 | .name = "pan", | ||
420 | .description = NULL_IF_CONFIG_SMALL("Remix channels with coefficients (panning)."), | ||
421 | .priv_size = sizeof(PanContext), | ||
422 | .priv_class = &pan_class, | ||
423 | .init = init, | ||
424 | .uninit = uninit, | ||
425 | FILTER_INPUTS(pan_inputs), | ||
426 | FILTER_OUTPUTS(ff_audio_default_filterpad), | ||
427 | FILTER_QUERY_FUNC(query_formats), | ||
428 | }; | ||
429 |