Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
52868 views
1
/*
2
* Copyright (c) 2002 Anders Johansson <[email protected]>
3
* Copyright (c) 2011 Clément Bœsch <u pkh me>
4
* Copyright (c) 2011 Nicolas George <[email protected]>
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
int64_t 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
57
static void skip_spaces(char **arg)
58
{
59
int len = 0;
60
61
sscanf(*arg, " %n", &len);
62
*arg += len;
63
}
64
65
static int parse_channel_name(char **arg, int *rchannel, int *rnamed)
66
{
67
char buf[8];
68
int len, i, channel_id = 0;
69
int64_t layout, layout0;
70
71
skip_spaces(arg);
72
/* try to parse a channel name, e.g. "FL" */
73
if (sscanf(*arg, "%7[A-Z]%n", buf, &len)) {
74
layout0 = layout = av_get_channel_layout(buf);
75
/* channel_id <- first set bit in layout */
76
for (i = 32; i > 0; i >>= 1) {
77
if (layout >= (int64_t)1 << i) {
78
channel_id += i;
79
layout >>= i;
80
}
81
}
82
/* reject layouts that are not a single channel */
83
if (channel_id >= MAX_CHANNELS || layout0 != (int64_t)1 << channel_id)
84
return AVERROR(EINVAL);
85
*rchannel = channel_id;
86
*rnamed = 1;
87
*arg += len;
88
return 0;
89
}
90
/* try to parse a channel number, e.g. "c2" */
91
if (sscanf(*arg, "c%d%n", &channel_id, &len) &&
92
channel_id >= 0 && channel_id < MAX_CHANNELS) {
93
*rchannel = channel_id;
94
*rnamed = 0;
95
*arg += len;
96
return 0;
97
}
98
return AVERROR(EINVAL);
99
}
100
101
static av_cold int init(AVFilterContext *ctx)
102
{
103
PanContext *const pan = ctx->priv;
104
char *arg, *arg0, *tokenizer, *args = av_strdup(pan->args);
105
int out_ch_id, in_ch_id, len, named, ret;
106
int nb_in_channels[2] = { 0, 0 }; // number of unnamed and named input channels
107
double gain;
108
109
if (!pan->args) {
110
av_log(ctx, AV_LOG_ERROR,
111
"pan filter needs a channel layout and a set "
112
"of channels definitions as parameter\n");
113
return AVERROR(EINVAL);
114
}
115
if (!args)
116
return AVERROR(ENOMEM);
117
arg = av_strtok(args, "|", &tokenizer);
118
ret = ff_parse_channel_layout(&pan->out_channel_layout,
119
&pan->nb_output_channels, arg, ctx);
120
if (ret < 0)
121
goto fail;
122
123
/* parse channel specifications */
124
while ((arg = arg0 = av_strtok(NULL, "|", &tokenizer))) {
125
/* channel name */
126
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
if (named) {
133
if (!((pan->out_channel_layout >> out_ch_id) & 1)) {
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
/* get the channel number in the output channel layout:
140
* out_channel_layout & ((1 << out_ch_id) - 1) are all the
141
* channels that come before out_ch_id,
142
* so their count is the index of out_ch_id */
143
out_ch_id = av_get_channel_layout_nb_channels(pan->out_channel_layout & (((int64_t)1 << out_ch_id) - 1));
144
}
145
if (out_ch_id < 0 || out_ch_id >= pan->nb_output_channels) {
146
av_log(ctx, AV_LOG_ERROR,
147
"Invalid out channel name \"%.8s\"\n", arg0);
148
ret = AVERROR(EINVAL);
149
goto fail;
150
}
151
skip_spaces(&arg);
152
if (*arg == '=') {
153
arg++;
154
} else if (*arg == '<') {
155
pan->need_renorm |= (int64_t)1 << out_ch_id;
156
arg++;
157
} else {
158
av_log(ctx, AV_LOG_ERROR,
159
"Syntax error after channel name in \"%.8s\"\n", arg0);
160
ret = AVERROR(EINVAL);
161
goto fail;
162
}
163
/* gains */
164
while (1) {
165
gain = 1;
166
if (sscanf(arg, "%lf%n *%n", &gain, &len, &len))
167
arg += len;
168
if (parse_channel_name(&arg, &in_ch_id, &named)){
169
av_log(ctx, AV_LOG_ERROR,
170
"Expected in channel name, got \"%.8s\"\n", arg);
171
ret = AVERROR(EINVAL);
172
goto fail;
173
}
174
nb_in_channels[named]++;
175
if (nb_in_channels[!named]) {
176
av_log(ctx, AV_LOG_ERROR,
177
"Can not mix named and numbered channels\n");
178
ret = AVERROR(EINVAL);
179
goto fail;
180
}
181
pan->gain[out_ch_id][in_ch_id] = gain;
182
skip_spaces(&arg);
183
if (!*arg)
184
break;
185
if (*arg != '+') {
186
av_log(ctx, AV_LOG_ERROR, "Syntax error near \"%.8s\"\n", arg);
187
ret = AVERROR(EINVAL);
188
goto fail;
189
}
190
arg++;
191
}
192
}
193
pan->need_renumber = !!nb_in_channels[1];
194
195
ret = 0;
196
fail:
197
av_free(args);
198
return ret;
199
}
200
201
static int are_gains_pure(const PanContext *pan)
202
{
203
int i, j;
204
205
for (i = 0; i < MAX_CHANNELS; i++) {
206
int nb_gain = 0;
207
208
for (j = 0; j < MAX_CHANNELS; j++) {
209
double gain = pan->gain[i][j];
210
211
/* channel mapping is effective only if 0% or 100% of a channel is
212
* selected... */
213
if (gain != 0. && gain != 1.)
214
return 0;
215
/* ...and if the output channel is only composed of one input */
216
if (gain && nb_gain++)
217
return 0;
218
}
219
}
220
return 1;
221
}
222
223
static int query_formats(AVFilterContext *ctx)
224
{
225
PanContext *pan = ctx->priv;
226
AVFilterLink *inlink = ctx->inputs[0];
227
AVFilterLink *outlink = ctx->outputs[0];
228
AVFilterFormats *formats = NULL;
229
AVFilterChannelLayouts *layouts;
230
int ret;
231
232
pan->pure_gains = are_gains_pure(pan);
233
/* libswr supports any sample and packing formats */
234
if ((ret = ff_set_common_formats(ctx, ff_all_formats(AVMEDIA_TYPE_AUDIO))) < 0)
235
return ret;
236
237
formats = ff_all_samplerates();
238
if ((ret = ff_set_common_samplerates(ctx, formats)) < 0)
239
return ret;
240
241
// inlink supports any channel layout
242
layouts = ff_all_channel_counts();
243
if ((ret = ff_channel_layouts_ref(layouts, &inlink->out_channel_layouts)) < 0)
244
return ret;
245
246
// outlink supports only requested output channel layout
247
layouts = NULL;
248
if ((ret = ff_add_channel_layout(&layouts,
249
pan->out_channel_layout ? pan->out_channel_layout :
250
FF_COUNT2LAYOUT(pan->nb_output_channels))) < 0)
251
return ret;
252
return ff_channel_layouts_ref(layouts, &outlink->in_channel_layouts);
253
}
254
255
static int config_props(AVFilterLink *link)
256
{
257
AVFilterContext *ctx = link->dst;
258
PanContext *pan = ctx->priv;
259
char buf[1024], *cur;
260
int i, j, k, r;
261
double t;
262
263
if (pan->need_renumber) {
264
// input channels were given by their name: renumber them
265
for (i = j = 0; i < MAX_CHANNELS; i++) {
266
if ((link->channel_layout >> i) & 1) {
267
for (k = 0; k < pan->nb_output_channels; k++)
268
pan->gain[k][j] = pan->gain[k][i];
269
j++;
270
}
271
}
272
}
273
274
// sanity check; can't be done in query_formats since the inlink
275
// channel layout is unknown at that time
276
if (link->channels > MAX_CHANNELS ||
277
pan->nb_output_channels > MAX_CHANNELS) {
278
av_log(ctx, AV_LOG_ERROR,
279
"af_pan support a maximum of %d channels. "
280
"Feel free to ask for a higher limit.\n", MAX_CHANNELS);
281
return AVERROR_PATCHWELCOME;
282
}
283
284
// init libswresample context
285
pan->swr = swr_alloc_set_opts(pan->swr,
286
pan->out_channel_layout, link->format, link->sample_rate,
287
link->channel_layout, link->format, link->sample_rate,
288
0, ctx);
289
if (!pan->swr)
290
return AVERROR(ENOMEM);
291
if (!link->channel_layout) {
292
if (av_opt_set_int(pan->swr, "ich", link->channels, 0) < 0)
293
return AVERROR(EINVAL);
294
}
295
if (!pan->out_channel_layout) {
296
if (av_opt_set_int(pan->swr, "och", pan->nb_output_channels, 0) < 0)
297
return AVERROR(EINVAL);
298
}
299
300
// gains are pure, init the channel mapping
301
if (pan->pure_gains) {
302
303
// get channel map from the pure gains
304
for (i = 0; i < pan->nb_output_channels; i++) {
305
int ch_id = -1;
306
for (j = 0; j < link->channels; j++) {
307
if (pan->gain[i][j]) {
308
ch_id = j;
309
break;
310
}
311
}
312
pan->channel_map[i] = ch_id;
313
}
314
315
av_opt_set_int(pan->swr, "icl", pan->out_channel_layout, 0);
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->channels; j++)
325
t += 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->channels; j++)
334
pan->gain[i][j] /= t;
335
}
336
av_opt_set_int(pan->swr, "icl", link->channel_layout, 0);
337
av_opt_set_int(pan->swr, "ocl", pan->out_channel_layout, 0);
338
swr_set_matrix(pan->swr, pan->gain[0], pan->gain[1] - pan->gain[0]);
339
}
340
341
r = swr_init(pan->swr);
342
if (r < 0)
343
return r;
344
345
// summary
346
for (i = 0; i < pan->nb_output_channels; i++) {
347
cur = buf;
348
for (j = 0; j < link->channels; j++) {
349
r = snprintf(cur, buf + sizeof(buf) - cur, "%s%.3g i%d",
350
j ? " + " : "", pan->gain[i][j], j);
351
cur += FFMIN(buf + sizeof(buf) - cur, r);
352
}
353
av_log(ctx, AV_LOG_VERBOSE, "o%d = %s\n", i, buf);
354
}
355
// add channel mapping summary if possible
356
if (pan->pure_gains) {
357
av_log(ctx, AV_LOG_INFO, "Pure channel mapping detected:");
358
for (i = 0; i < pan->nb_output_channels; i++)
359
if (pan->channel_map[i] < 0)
360
av_log(ctx, AV_LOG_INFO, " M");
361
else
362
av_log(ctx, AV_LOG_INFO, " %d", pan->channel_map[i]);
363
av_log(ctx, AV_LOG_INFO, "\n");
364
return 0;
365
}
366
return 0;
367
}
368
369
static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
370
{
371
int ret;
372
int n = insamples->nb_samples;
373
AVFilterLink *const outlink = inlink->dst->outputs[0];
374
AVFrame *outsamples = ff_get_audio_buffer(outlink, n);
375
PanContext *pan = inlink->dst->priv;
376
377
if (!outsamples)
378
return AVERROR(ENOMEM);
379
swr_convert(pan->swr, outsamples->extended_data, n,
380
(void *)insamples->extended_data, n);
381
av_frame_copy_props(outsamples, insamples);
382
outsamples->channel_layout = outlink->channel_layout;
383
av_frame_set_channels(outsamples, outlink->channels);
384
385
ret = ff_filter_frame(outlink, outsamples);
386
av_frame_free(&insamples);
387
return ret;
388
}
389
390
static av_cold void uninit(AVFilterContext *ctx)
391
{
392
PanContext *pan = ctx->priv;
393
swr_free(&pan->swr);
394
}
395
396
#define OFFSET(x) offsetof(PanContext, x)
397
398
static const AVOption pan_options[] = {
399
{ "args", NULL, OFFSET(args), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM },
400
{ NULL }
401
};
402
403
AVFILTER_DEFINE_CLASS(pan);
404
405
static const AVFilterPad pan_inputs[] = {
406
{
407
.name = "default",
408
.type = AVMEDIA_TYPE_AUDIO,
409
.config_props = config_props,
410
.filter_frame = filter_frame,
411
},
412
{ NULL }
413
};
414
415
static const AVFilterPad pan_outputs[] = {
416
{
417
.name = "default",
418
.type = AVMEDIA_TYPE_AUDIO,
419
},
420
{ NULL }
421
};
422
423
AVFilter ff_af_pan = {
424
.name = "pan",
425
.description = NULL_IF_CONFIG_SMALL("Remix channels with coefficients (panning)."),
426
.priv_size = sizeof(PanContext),
427
.priv_class = &pan_class,
428
.init = init,
429
.uninit = uninit,
430
.query_formats = query_formats,
431
.inputs = pan_inputs,
432
.outputs = pan_outputs,
433
};
434
435