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