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