1 /*
2 * This file is part of FFmpeg.
3 *
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 /**
20 * @file
21 * Audio join filter
22 *
23 * Join multiple audio inputs as different channels in
24 * a single output
25 */
26
31
37
39 int input;
///< input stream index
40 int in_channel_idx;
///< index of in_channel in the input stream data
41 uint64_t
in_channel;
///< layout describing the input channel
42 uint64_t
out_channel;
///< layout describing the output channel
44
47
52
54
57
58 /**
59 * Temporary storage for input frames, until we get one on each input.
60 */
62
63 /**
64 * Temporary storage for buffer references, for assembling the output frame.
65 */
68
69 #define OFFSET(x) offsetof(JoinContext, x)
70 #define A AV_OPT_FLAG_AUDIO_PARAM
71 #define F AV_OPT_FLAG_FILTERING_PARAM
74 { "channel_layout", "Channel layout of the "
76 { "map", "A comma-separated list of channels maps in the format "
77 "'input_stream.input_channel-output_channel.",
80 };
81
83
85 {
87 char separator = '|';
89
90 while (cur && *cur) {
91 char *sep, *next, *p;
92 uint64_t in_channel = 0, out_channel = 0;
93 int input_idx, out_ch_idx, in_ch_idx;
94
95 next = strchr(cur, separator);
96 if (next)
97 *next++ = 0;
98
99 /* split the map into input and output parts */
100 if (!(sep = strchr(cur, '-'))) {
102 "map '%s'\n", cur);
104 }
105 *sep++ = 0;
106
107 #define PARSE_CHANNEL(str, var, inout) \
108 if (!(var = av_get_channel_layout(str))) { \
109 av_log(ctx, AV_LOG_ERROR, "Invalid " inout " channel: %s.\n", str);\
110 return AVERROR(EINVAL); \
111 } \
112 if (av_get_channel_layout_nb_channels(var) != 1) { \
113 av_log(ctx, AV_LOG_ERROR, "Channel map describes more than one " \
114 inout " channel.\n"); \
115 return AVERROR(EINVAL); \
116 }
117
118 /* parse output channel */
120 if (!(out_channel &
s->channel_layout)) {
122 "requested channel layout.\n", sep);
124 }
125
127 out_channel);
128 if (
s->channels[out_ch_idx].input >= 0) {
130 "'%s'.\n", sep);
132 }
133
134 /* parse input channel */
135 input_idx = strtol(cur, &cur, 0);
136 if (input_idx < 0 || input_idx >=
s->inputs) {
138 input_idx);
140 }
141
142 if (*cur)
143 cur++;
144
145 in_ch_idx = strtol(cur, &p, 0);
146 if (p == cur) {
147 /* channel specifier is not a number,
148 * try to parse as channel name */
150 }
151
152 s->channels[out_ch_idx].input = input_idx;
153 if (in_channel)
154 s->channels[out_ch_idx].in_channel = in_channel;
155 else
156 s->channels[out_ch_idx].in_channel_idx = in_ch_idx;
157
158 cur = next;
159 }
160 return 0;
161 }
162
164 {
167
170 s->channel_layout_str);
172 }
173
175 s->channels =
av_calloc(
s->nb_channels,
sizeof(*
s->channels));
176 s->buffers =
av_calloc(
s->nb_channels,
sizeof(*
s->buffers));
177 s->input_frames =
av_calloc(
s->inputs,
sizeof(*
s->input_frames));
178 if (!
s->channels || !
s->buffers|| !
s->input_frames)
180
181 for (
i = 0;
i <
s->nb_channels;
i++) {
183 s->channels[
i].input = -1;
184 }
185
188
189 for (
i = 0;
i <
s->inputs;
i++) {
191
196
199 }
200
201 return 0;
202 }
203
205 {
208
209 for (
i = 0;
i <
s->inputs &&
s->input_frames;
i++) {
211 }
212
216 }
217
219 {
223
227
228 for (
i = 0;
i <
ctx->nb_inputs;
i++) {
232 }
233
237
238 return 0;
239 }
240
243 {
245
246 for (
i = 0;
i <
ctx->nb_inputs;
i++) {
248
254 return;
255 }
256 }
257 }
258
261 {
263
264 for (
i = 0;
i <
ctx->nb_inputs;
i++) {
266
269
273 return;
274 }
275 }
276 }
277
279 {
282 uint64_t *
inputs;
// nth element tracks which channels are used from nth input
284
285 /* initialize inputs to user-specified mappings */
288 for (
i = 0;
i <
s->nb_channels;
i++) {
291
293 continue;
294
296
300
307 }
308
310 }
311
312 /* guess channel maps when not explicitly defined */
313 /* first try unused matching channels */
314 for (
i = 0;
i <
s->nb_channels;
i++) {
316
319 }
320
321 /* if the above failed, try to find _any_ unused input channel */
322 for (
i = 0;
i <
s->nb_channels;
i++) {
324
327
330 "output channel '%s'.\n",
333 }
334
337 }
338
339 /* print mappings */
341 for (
i = 0;
i <
s->nb_channels;
i++) {
346 }
348
349 for (
i = 0;
i <
ctx->nb_inputs;
i++) {
353 }
354
358 }
359
361 {
365 int linesize = INT_MAX;
366 int nb_samples = INT_MAX;
367 int nb_buffers = 0;
369
370 for (
i = 0;
i <
ctx->nb_inputs;
i++) {
371 if (!
s->input_frames[
i]) {
372 nb_samples = 0;
373 break;
374 } else {
375 nb_samples =
FFMIN(nb_samples,
s->input_frames[
i]->nb_samples);
376 }
377 }
378 if (!nb_samples)
379 goto eof;
380
381 /* setup the output frame */
387 sizeof(*
frame->extended_data));
388 if (!
frame->extended_data) {
391 }
392 }
393
394 /* copy the data pointers */
395 for (
i = 0;
i <
s->nb_channels;
i++) {
399
402
403 /* add the buffer where this plan is stored to the list if it's
404 * not already there */
406 if (!buf) {
409 }
410 for (j = 0; j < nb_buffers; j++)
411 if (
s->buffers[j]->buffer == buf->
buffer)
412 break;
414 s->buffers[nb_buffers++] = buf;
415 }
416
417 /* create references to the buffers we copied to output */
421 sizeof(*
frame->extended_buf));
422 if (!
frame->extended_buf) {
423 frame->nb_extended_buf = 0;
426 }
427 }
433 }
434 }
435 for (
i = 0;
i <
frame->nb_extended_buf;
i++) {
438 if (!
frame->extended_buf[
i]) {
441 }
442 }
443
444 frame->nb_samples = nb_samples;
449 frame->pts =
s->input_frames[0]->pts;
450 frame->linesize[0] = linesize;
454 }
455
460
461 for (
i = 0;
i <
ctx->nb_inputs;
i++)
463
465
469 eof:
470 for (
i = 0;
i <
ctx->nb_inputs;
i++) {
473 !
s->input_frames[
i]) {
475 }
476 }
477
478 return 0;
479 }
480
482 {
485 int nb_samples = 0;
487
489
490 if (!
s->input_frames[0]) {
496 return 0;
497 }
498
501 return 0;
502 }
503 }
504
505 nb_samples =
s->input_frames[0]->nb_samples;
506
507 for (
i = 1;
i <
ctx->nb_inputs && nb_samples > 0;
i++) {
508 if (
s->input_frames[
i])
509 continue;
515 return 0;
516 }
517
518 if (!
s->input_frames[
i]) {
520 return 0;
521 }
522 }
523
525 }
526
528 {
532 },
533 };
534
538 "multi-channel output."),
540 .priv_class = &join_class,
548 };