FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
af_join.c
Go to the documentation of this file.
1 /*
2  *
3  * This file is part of Libav.
4  *
5  * Libav is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * Libav is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with Libav; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 /**
21  * @file
22  * Audio join filter
23  *
24  * Join multiple audio inputs as different channels in
25  * a single output
26  */
27 
28 #include "libavutil/avassert.h"
30 #include "libavutil/common.h"
31 #include "libavutil/opt.h"
32 
33 #include "audio.h"
34 #include "avfilter.h"
35 #include "formats.h"
36 #include "internal.h"
37 
38 typedef struct ChannelMap {
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
43 } ChannelMap;
44 
45 typedef struct JoinContext {
46  const AVClass *class;
47 
48  int inputs;
49  char *map;
51  uint64_t channel_layout;
52 
55 
56  /**
57  * Temporary storage for input frames, until we get one on each input.
58  */
60 
61  /**
62  * Temporary storage for buffer references, for assembling the output frame.
63  */
65 } JoinContext;
66 
67 #define OFFSET(x) offsetof(JoinContext, x)
68 #define A AV_OPT_FLAG_AUDIO_PARAM
69 #define F AV_OPT_FLAG_FILTERING_PARAM
70 static const AVOption join_options[] = {
71  { "inputs", "Number of input streams.", OFFSET(inputs), AV_OPT_TYPE_INT, { .i64 = 2 }, 1, INT_MAX, A|F },
72  { "channel_layout", "Channel layout of the "
73  "output stream.", OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, {.str = "stereo"}, 0, 0, A|F },
74  { "map", "A comma-separated list of channels maps in the format "
75  "'input_stream.input_channel-output_channel.",
76  OFFSET(map), AV_OPT_TYPE_STRING, .flags = A|F },
77  { NULL },
78 };
79 
81 
83 {
84  AVFilterContext *ctx = link->dst;
85  JoinContext *s = ctx->priv;
86  int i;
87 
88  for (i = 0; i < ctx->nb_inputs; i++)
89  if (link == ctx->inputs[i])
90  break;
91  av_assert0(i < ctx->nb_inputs);
92  av_assert0(!s->input_frames[i]);
93  s->input_frames[i] = frame;
94 
95  return 0;
96 }
97 
98 static int parse_maps(AVFilterContext *ctx)
99 {
100  JoinContext *s = ctx->priv;
101  char separator = '|';
102  char *cur = s->map;
103 
104 #if FF_API_OLD_FILTER_OPTS
105  if (cur && strchr(cur, ',')) {
106  av_log(ctx, AV_LOG_WARNING, "This syntax is deprecated, use '|' to "
107  "separate the mappings.\n");
108  separator = ',';
109  }
110 #endif
111 
112  while (cur && *cur) {
113  char *sep, *next, *p;
114  uint64_t in_channel = 0, out_channel = 0;
115  int input_idx, out_ch_idx, in_ch_idx;
116 
117  next = strchr(cur, separator);
118  if (next)
119  *next++ = 0;
120 
121  /* split the map into input and output parts */
122  if (!(sep = strchr(cur, '-'))) {
123  av_log(ctx, AV_LOG_ERROR, "Missing separator '-' in channel "
124  "map '%s'\n", cur);
125  return AVERROR(EINVAL);
126  }
127  *sep++ = 0;
128 
129 #define PARSE_CHANNEL(str, var, inout) \
130  if (!(var = av_get_channel_layout(str))) { \
131  av_log(ctx, AV_LOG_ERROR, "Invalid " inout " channel: %s.\n", str);\
132  return AVERROR(EINVAL); \
133  } \
134  if (av_get_channel_layout_nb_channels(var) != 1) { \
135  av_log(ctx, AV_LOG_ERROR, "Channel map describes more than one " \
136  inout " channel.\n"); \
137  return AVERROR(EINVAL); \
138  }
139 
140  /* parse output channel */
141  PARSE_CHANNEL(sep, out_channel, "output");
142  if (!(out_channel & s->channel_layout)) {
143  av_log(ctx, AV_LOG_ERROR, "Output channel '%s' is not present in "
144  "requested channel layout.\n", sep);
145  return AVERROR(EINVAL);
146  }
147 
149  out_channel);
150  if (s->channels[out_ch_idx].input >= 0) {
151  av_log(ctx, AV_LOG_ERROR, "Multiple maps for output channel "
152  "'%s'.\n", sep);
153  return AVERROR(EINVAL);
154  }
155 
156  /* parse input channel */
157  input_idx = strtol(cur, &cur, 0);
158  if (input_idx < 0 || input_idx >= s->inputs) {
159  av_log(ctx, AV_LOG_ERROR, "Invalid input stream index: %d.\n",
160  input_idx);
161  return AVERROR(EINVAL);
162  }
163 
164  if (*cur)
165  cur++;
166 
167  in_ch_idx = strtol(cur, &p, 0);
168  if (p == cur) {
169  /* channel specifier is not a number,
170  * try to parse as channel name */
171  PARSE_CHANNEL(cur, in_channel, "input");
172  }
173 
174  s->channels[out_ch_idx].input = input_idx;
175  if (in_channel)
176  s->channels[out_ch_idx].in_channel = in_channel;
177  else
178  s->channels[out_ch_idx].in_channel_idx = in_ch_idx;
179 
180  cur = next;
181  }
182  return 0;
183 }
184 
186 {
187  JoinContext *s = ctx->priv;
188  int ret, i;
189 
191  av_log(ctx, AV_LOG_ERROR, "Error parsing channel layout '%s'.\n",
192  s->channel_layout_str);
193  ret = AVERROR(EINVAL);
194  goto fail;
195  }
196 
198  s->channels = av_mallocz(sizeof(*s->channels) * s->nb_channels);
199  s->buffers = av_mallocz(sizeof(*s->buffers) * s->nb_channels);
200  s->input_frames = av_mallocz(sizeof(*s->input_frames) * s->inputs);
201  if (!s->channels || !s->buffers|| !s->input_frames) {
202  ret = AVERROR(ENOMEM);
203  goto fail;
204  }
205 
206  for (i = 0; i < s->nb_channels; i++) {
208  s->channels[i].input = -1;
209  }
210 
211  if ((ret = parse_maps(ctx)) < 0)
212  goto fail;
213 
214  for (i = 0; i < s->inputs; i++) {
215  char name[32];
216  AVFilterPad pad = { 0 };
217 
218  snprintf(name, sizeof(name), "input%d", i);
219  pad.type = AVMEDIA_TYPE_AUDIO;
220  pad.name = av_strdup(name);
222 
223  pad.needs_fifo = 1;
224 
225  ff_insert_inpad(ctx, i, &pad);
226  }
227 
228 fail:
229  av_opt_free(s);
230  return ret;
231 }
232 
234 {
235  JoinContext *s = ctx->priv;
236  int i;
237 
238  for (i = 0; i < ctx->nb_inputs; i++) {
239  av_freep(&ctx->input_pads[i].name);
240  av_frame_free(&s->input_frames[i]);
241  }
242 
243  av_freep(&s->channels);
244  av_freep(&s->buffers);
245  av_freep(&s->input_frames);
246 }
247 
249 {
250  JoinContext *s = ctx->priv;
252  int i;
253 
256 
257  for (i = 0; i < ctx->nb_inputs; i++)
259  &ctx->inputs[i]->out_channel_layouts);
260 
263 
264  return 0;
265 }
266 
268  uint64_t *inputs)
269 {
270  int i;
271 
272  for (i = 0; i < ctx->nb_inputs; i++) {
273  AVFilterLink *link = ctx->inputs[i];
274 
275  if (ch->out_channel & link->channel_layout &&
276  !(ch->out_channel & inputs[i])) {
277  ch->input = i;
278  ch->in_channel = ch->out_channel;
279  inputs[i] |= ch->out_channel;
280  return;
281  }
282  }
283 }
284 
286  uint64_t *inputs)
287 {
288  int i;
289 
290  for (i = 0; i < ctx->nb_inputs; i++) {
291  AVFilterLink *link = ctx->inputs[i];
292 
293  if ((inputs[i] & link->channel_layout) != link->channel_layout) {
294  uint64_t unused = link->channel_layout & ~inputs[i];
295 
296  ch->input = i;
298  inputs[i] |= ch->in_channel;
299  return;
300  }
301  }
302 }
303 
304 static int join_config_output(AVFilterLink *outlink)
305 {
306  AVFilterContext *ctx = outlink->src;
307  JoinContext *s = ctx->priv;
308  uint64_t *inputs; // nth element tracks which channels are used from nth input
309  int i, ret = 0;
310 
311  /* initialize inputs to user-specified mappings */
312  if (!(inputs = av_mallocz(sizeof(*inputs) * ctx->nb_inputs)))
313  return AVERROR(ENOMEM);
314  for (i = 0; i < s->nb_channels; i++) {
315  ChannelMap *ch = &s->channels[i];
316  AVFilterLink *inlink;
317 
318  if (ch->input < 0)
319  continue;
320 
321  inlink = ctx->inputs[ch->input];
322 
323  if (!ch->in_channel)
325  ch->in_channel_idx);
326 
327  if (!(ch->in_channel & inlink->channel_layout)) {
328  av_log(ctx, AV_LOG_ERROR, "Requested channel %s is not present in "
329  "input stream #%d.\n", av_get_channel_name(ch->in_channel),
330  ch->input);
331  ret = AVERROR(EINVAL);
332  goto fail;
333  }
334 
335  inputs[ch->input] |= ch->in_channel;
336  }
337 
338  /* guess channel maps when not explicitly defined */
339  /* first try unused matching channels */
340  for (i = 0; i < s->nb_channels; i++) {
341  ChannelMap *ch = &s->channels[i];
342 
343  if (ch->input < 0)
344  guess_map_matching(ctx, ch, inputs);
345  }
346 
347  /* if the above failed, try to find _any_ unused input channel */
348  for (i = 0; i < s->nb_channels; i++) {
349  ChannelMap *ch = &s->channels[i];
350 
351  if (ch->input < 0)
352  guess_map_any(ctx, ch, inputs);
353 
354  if (ch->input < 0) {
355  av_log(ctx, AV_LOG_ERROR, "Could not find input channel for "
356  "output channel '%s'.\n",
358  goto fail;
359  }
360 
362  ch->in_channel);
363  }
364 
365  /* print mappings */
366  av_log(ctx, AV_LOG_VERBOSE, "mappings: ");
367  for (i = 0; i < s->nb_channels; i++) {
368  ChannelMap *ch = &s->channels[i];
369  av_log(ctx, AV_LOG_VERBOSE, "%d.%s => %s ", ch->input,
372  }
373  av_log(ctx, AV_LOG_VERBOSE, "\n");
374 
375  for (i = 0; i < ctx->nb_inputs; i++) {
376  if (!inputs[i])
377  av_log(ctx, AV_LOG_WARNING, "No channels are used from input "
378  "stream %d.\n", i);
379  }
380 
381 fail:
382  av_freep(&inputs);
383  return ret;
384 }
385 
386 static int join_request_frame(AVFilterLink *outlink)
387 {
388  AVFilterContext *ctx = outlink->src;
389  JoinContext *s = ctx->priv;
390  AVFrame *frame;
391  int linesize = INT_MAX;
392  int nb_samples = 0;
393  int nb_buffers = 0;
394  int i, j, ret;
395 
396  /* get a frame on each input */
397  for (i = 0; i < ctx->nb_inputs; i++) {
398  AVFilterLink *inlink = ctx->inputs[i];
399 
400  if (!s->input_frames[i] &&
401  (ret = ff_request_frame(inlink)) < 0)
402  return ret;
403 
404  /* request the same number of samples on all inputs */
405  if (i == 0) {
406  nb_samples = s->input_frames[0]->nb_samples;
407 
408  for (j = 1; !i && j < ctx->nb_inputs; j++)
409  ctx->inputs[j]->request_samples = nb_samples;
410  }
411  }
412 
413  /* setup the output frame */
414  frame = av_frame_alloc();
415  if (!frame)
416  return AVERROR(ENOMEM);
417  if (s->nb_channels > FF_ARRAY_ELEMS(frame->data)) {
418  frame->extended_data = av_mallocz(s->nb_channels *
419  sizeof(*frame->extended_data));
420  if (!frame->extended_data) {
421  ret = AVERROR(ENOMEM);
422  goto fail;
423  }
424  }
425 
426  /* copy the data pointers */
427  for (i = 0; i < s->nb_channels; i++) {
428  ChannelMap *ch = &s->channels[i];
429  AVFrame *cur = s->input_frames[ch->input];
430  AVBufferRef *buf;
431 
432  frame->extended_data[i] = cur->extended_data[ch->in_channel_idx];
433  linesize = FFMIN(linesize, cur->linesize[0]);
434 
435  /* add the buffer where this plan is stored to the list if it's
436  * not already there */
438  if (!buf) {
439  ret = AVERROR(EINVAL);
440  goto fail;
441  }
442  for (j = 0; j < nb_buffers; j++)
443  if (s->buffers[j]->buffer == buf->buffer)
444  break;
445  if (j == i)
446  s->buffers[nb_buffers++] = buf;
447  }
448 
449  /* create references to the buffers we copied to output */
450  if (nb_buffers > FF_ARRAY_ELEMS(frame->buf)) {
451  frame->nb_extended_buf = nb_buffers - FF_ARRAY_ELEMS(frame->buf);
452  frame->extended_buf = av_mallocz(sizeof(*frame->extended_buf) *
453  frame->nb_extended_buf);
454  if (!frame->extended_buf) {
455  frame->nb_extended_buf = 0;
456  ret = AVERROR(ENOMEM);
457  goto fail;
458  }
459  }
460  for (i = 0; i < FFMIN(FF_ARRAY_ELEMS(frame->buf), nb_buffers); i++) {
461  frame->buf[i] = av_buffer_ref(s->buffers[i]);
462  if (!frame->buf[i]) {
463  ret = AVERROR(ENOMEM);
464  goto fail;
465  }
466  }
467  for (i = 0; i < frame->nb_extended_buf; i++) {
468  frame->extended_buf[i] = av_buffer_ref(s->buffers[i +
469  FF_ARRAY_ELEMS(frame->buf)]);
470  if (!frame->extended_buf[i]) {
471  ret = AVERROR(ENOMEM);
472  goto fail;
473  }
474  }
475 
476  frame->nb_samples = nb_samples;
477  frame->channel_layout = outlink->channel_layout;
478  av_frame_set_channels(frame, outlink->channels);
479  frame->format = outlink->format;
480  frame->sample_rate = outlink->sample_rate;
481  frame->pts = s->input_frames[0]->pts;
482  frame->linesize[0] = linesize;
483  if (frame->data != frame->extended_data) {
484  memcpy(frame->data, frame->extended_data, sizeof(*frame->data) *
485  FFMIN(FF_ARRAY_ELEMS(frame->data), s->nb_channels));
486  }
487 
488  ret = ff_filter_frame(outlink, frame);
489 
490  for (i = 0; i < ctx->nb_inputs; i++)
491  av_frame_free(&s->input_frames[i]);
492 
493  return ret;
494 
495 fail:
496  av_frame_free(&frame);
497  return ret;
498 }
499 
501  {
502  .name = "default",
503  .type = AVMEDIA_TYPE_AUDIO,
504  .config_props = join_config_output,
505  .request_frame = join_request_frame,
506  },
507  { NULL }
508 };
509 
511  .name = "join",
512  .description = NULL_IF_CONFIG_SMALL("Join multiple audio streams into "
513  "multi-channel output."),
514  .priv_size = sizeof(JoinContext),
515  .priv_class = &join_class,
516 
517  .init = join_init,
518  .uninit = join_uninit,
520 
521  .inputs = NULL,
522  .outputs = avfilter_af_join_outputs,
523 
525 };