Libav
af_channelmap.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2012 Google, Inc.
3  *
4  * This file is part of Libav.
5  *
6  * Libav is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * Libav is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with Libav; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
26 #include <ctype.h>
27 
28 #include "libavutil/avstring.h"
30 #include "libavutil/common.h"
31 #include "libavutil/mathematics.h"
32 #include "libavutil/opt.h"
33 #include "libavutil/samplefmt.h"
34 
35 #include "audio.h"
36 #include "avfilter.h"
37 #include "formats.h"
38 #include "internal.h"
39 
40 struct ChannelMap {
41  uint64_t in_channel;
42  uint64_t out_channel;
45 };
46 
55 };
56 
57 #define MAX_CH 64
58 typedef struct ChannelMapContext {
59  const AVClass *class;
61  char *mapping_str;
63  uint64_t output_layout;
65  int nch;
68 
69 #define OFFSET(x) offsetof(ChannelMapContext, x)
70 #define A AV_OPT_FLAG_AUDIO_PARAM
71 static const AVOption options[] = {
72  { "map", "A comma-separated list of input channel numbers in output order.",
73  OFFSET(mapping_str), AV_OPT_TYPE_STRING, .flags = A },
74  { "channel_layout", "Output channel layout.",
75  OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, .flags = A },
76  { NULL },
77 };
78 
79 static const AVClass channelmap_class = {
80  .class_name = "channel map filter",
81  .item_name = av_default_item_name,
82  .option = options,
83  .version = LIBAVUTIL_VERSION_INT,
84 };
85 
86 static char* split(char *message, char delim) {
87  char *next = strchr(message, delim);
88  if (next)
89  *next++ = '\0';
90  return next;
91 }
92 
93 static int get_channel_idx(char **map, int *ch, char delim, int max_ch)
94 {
95  char *next = split(*map, delim);
96  int len;
97  int n = 0;
98  if (!next && delim == '-')
99  return AVERROR(EINVAL);
100  len = strlen(*map);
101  sscanf(*map, "%d%n", ch, &n);
102  if (n != len)
103  return AVERROR(EINVAL);
104  if (*ch < 0 || *ch > max_ch)
105  return AVERROR(EINVAL);
106  *map = next;
107  return 0;
108 }
109 
110 static int get_channel(char **map, uint64_t *ch, char delim)
111 {
112  char *next = split(*map, delim);
113  if (!next && delim == '-')
114  return AVERROR(EINVAL);
115  *ch = av_get_channel_layout(*map);
116  if (av_get_channel_layout_nb_channels(*ch) != 1)
117  return AVERROR(EINVAL);
118  *map = next;
119  return 0;
120 }
121 
123 {
124  ChannelMapContext *s = ctx->priv;
125  char *mapping, separator = '|';
126  int map_entries = 0;
127  char buf[256];
128  enum MappingMode mode;
129  uint64_t out_ch_mask = 0;
130  int i;
131 
132  mapping = s->mapping_str;
133 
134  if (!mapping) {
135  mode = MAP_NONE;
136  } else {
137  char *dash = strchr(mapping, '-');
138  if (!dash) { // short mapping
139  if (av_isdigit(*mapping))
140  mode = MAP_ONE_INT;
141  else
142  mode = MAP_ONE_STR;
143  } else if (av_isdigit(*mapping)) {
144  if (av_isdigit(*(dash+1)))
145  mode = MAP_PAIR_INT_INT;
146  else
147  mode = MAP_PAIR_INT_STR;
148  } else {
149  if (av_isdigit(*(dash+1)))
150  mode = MAP_PAIR_STR_INT;
151  else
152  mode = MAP_PAIR_STR_STR;
153  }
154 #if FF_API_OLD_FILTER_OPTS
155  if (strchr(mapping, ',')) {
156  av_log(ctx, AV_LOG_WARNING, "This syntax is deprecated, use "
157  "'|' to separate the mappings.\n");
158  separator = ',';
159  }
160 #endif
161  }
162 
163  if (mode != MAP_NONE) {
164  char *sep = mapping;
165  map_entries = 1;
166  while ((sep = strchr(sep, separator))) {
167  if (*++sep) // Allow trailing comma
168  map_entries++;
169  }
170  }
171 
172  if (map_entries > MAX_CH) {
173  av_log(ctx, AV_LOG_ERROR, "Too many channels mapped: '%d'.\n", map_entries);
174  return AVERROR(EINVAL);
175  }
176 
177  for (i = 0; i < map_entries; i++) {
178  int in_ch_idx = -1, out_ch_idx = -1;
179  uint64_t in_ch = 0, out_ch = 0;
180  static const char err[] = "Failed to parse channel map\n";
181  switch (mode) {
182  case MAP_ONE_INT:
183  if (get_channel_idx(&mapping, &in_ch_idx, separator, MAX_CH) < 0) {
184  av_log(ctx, AV_LOG_ERROR, err);
185  return AVERROR(EINVAL);
186  }
187  s->map[i].in_channel_idx = in_ch_idx;
188  s->map[i].out_channel_idx = i;
189  break;
190  case MAP_ONE_STR:
191  if (get_channel(&mapping, &in_ch, separator) < 0) {
192  av_log(ctx, AV_LOG_ERROR, err);
193  return AVERROR(EINVAL);
194  }
195  s->map[i].in_channel = in_ch;
196  s->map[i].out_channel_idx = i;
197  break;
198  case MAP_PAIR_INT_INT:
199  if (get_channel_idx(&mapping, &in_ch_idx, '-', MAX_CH) < 0 ||
200  get_channel_idx(&mapping, &out_ch_idx, separator, MAX_CH) < 0) {
201  av_log(ctx, AV_LOG_ERROR, err);
202  return AVERROR(EINVAL);
203  }
204  s->map[i].in_channel_idx = in_ch_idx;
205  s->map[i].out_channel_idx = out_ch_idx;
206  break;
207  case MAP_PAIR_INT_STR:
208  if (get_channel_idx(&mapping, &in_ch_idx, '-', MAX_CH) < 0 ||
209  get_channel(&mapping, &out_ch, separator) < 0 ||
210  out_ch & out_ch_mask) {
211  av_log(ctx, AV_LOG_ERROR, err);
212  return AVERROR(EINVAL);
213  }
214  s->map[i].in_channel_idx = in_ch_idx;
215  s->map[i].out_channel = out_ch;
216  out_ch_mask |= out_ch;
217  break;
218  case MAP_PAIR_STR_INT:
219  if (get_channel(&mapping, &in_ch, '-') < 0 ||
220  get_channel_idx(&mapping, &out_ch_idx, separator, MAX_CH) < 0) {
221  av_log(ctx, AV_LOG_ERROR, err);
222  return AVERROR(EINVAL);
223  }
224  s->map[i].in_channel = in_ch;
225  s->map[i].out_channel_idx = out_ch_idx;
226  break;
227  case MAP_PAIR_STR_STR:
228  if (get_channel(&mapping, &in_ch, '-') < 0 ||
229  get_channel(&mapping, &out_ch, separator) < 0 ||
230  out_ch & out_ch_mask) {
231  av_log(ctx, AV_LOG_ERROR, err);
232  return AVERROR(EINVAL);
233  }
234  s->map[i].in_channel = in_ch;
235  s->map[i].out_channel = out_ch;
236  out_ch_mask |= out_ch;
237  break;
238  }
239  }
240  s->mode = mode;
241  s->nch = map_entries;
242  s->output_layout = out_ch_mask ? out_ch_mask :
243  av_get_default_channel_layout(map_entries);
244 
245  if (s->channel_layout_str) {
246  uint64_t fmt;
247  if ((fmt = av_get_channel_layout(s->channel_layout_str)) == 0) {
248  av_log(ctx, AV_LOG_ERROR, "Error parsing channel layout: '%s'.\n",
249  s->channel_layout_str);
250  return AVERROR(EINVAL);
251  }
252  if (mode == MAP_NONE) {
253  int i;
255  for (i = 0; i < s->nch; i++) {
256  s->map[i].in_channel_idx = i;
257  s->map[i].out_channel_idx = i;
258  }
259  } else if (out_ch_mask && out_ch_mask != fmt) {
260  av_get_channel_layout_string(buf, sizeof(buf), 0, out_ch_mask);
261  av_log(ctx, AV_LOG_ERROR,
262  "Output channel layout '%s' does not match the list of channel mapped: '%s'.\n",
263  s->channel_layout_str, buf);
264  return AVERROR(EINVAL);
265  } else if (s->nch != av_get_channel_layout_nb_channels(fmt)) {
266  av_log(ctx, AV_LOG_ERROR,
267  "Output channel layout %s does not match the number of channels mapped %d.\n",
268  s->channel_layout_str, s->nch);
269  return AVERROR(EINVAL);
270  }
271  s->output_layout = fmt;
272  }
273  if (!s->output_layout) {
274  av_log(ctx, AV_LOG_ERROR, "Output channel layout is not set and "
275  "cannot be guessed from the maps.\n");
276  return AVERROR(EINVAL);
277  }
278 
280 
281  if (mode == MAP_PAIR_INT_STR || mode == MAP_PAIR_STR_STR) {
282  for (i = 0; i < s->nch; i++) {
284  s->output_layout, s->map[i].out_channel);
285  }
286  }
287 
288  return 0;
289 }
290 
292 {
293  ChannelMapContext *s = ctx->priv;
294 
299 
300  return 0;
301 }
302 
304 {
305  AVFilterContext *ctx = inlink->dst;
306  AVFilterLink *outlink = ctx->outputs[0];
307  const ChannelMapContext *s = ctx->priv;
308  const int nch_in = av_get_channel_layout_nb_channels(inlink->channel_layout);
309  const int nch_out = s->nch;
310  int ch;
311  uint8_t *source_planes[MAX_CH];
312 
313  memcpy(source_planes, buf->extended_data,
314  nch_in * sizeof(source_planes[0]));
315 
316  if (nch_out > nch_in) {
317  if (nch_out > FF_ARRAY_ELEMS(buf->data)) {
318  uint8_t **new_extended_data =
319  av_mallocz(nch_out * sizeof(*buf->extended_data));
320  if (!new_extended_data) {
321  av_frame_free(&buf);
322  return AVERROR(ENOMEM);
323  }
324  if (buf->extended_data == buf->data) {
325  buf->extended_data = new_extended_data;
326  } else {
327  av_free(buf->extended_data);
328  buf->extended_data = new_extended_data;
329  }
330  } else if (buf->extended_data != buf->data) {
331  av_free(buf->extended_data);
332  buf->extended_data = buf->data;
333  }
334  }
335 
336  for (ch = 0; ch < nch_out; ch++) {
337  buf->extended_data[s->map[ch].out_channel_idx] =
338  source_planes[s->map[ch].in_channel_idx];
339  }
340 
341  if (buf->data != buf->extended_data)
342  memcpy(buf->data, buf->extended_data,
343  FFMIN(FF_ARRAY_ELEMS(buf->data), nch_out) * sizeof(buf->data[0]));
344 
345  return ff_filter_frame(outlink, buf);
346 }
347 
349 {
350  AVFilterContext *ctx = inlink->dst;
351  ChannelMapContext *s = ctx->priv;
353  int i, err = 0;
354  const char *channel_name;
355  char layout_name[256];
356 
357  for (i = 0; i < s->nch; i++) {
358  struct ChannelMap *m = &s->map[i];
359 
360  if (s->mode == MAP_PAIR_STR_INT || s->mode == MAP_PAIR_STR_STR) {
362  inlink->channel_layout, m->in_channel);
363  }
364 
365  if (m->in_channel_idx < 0 || m->in_channel_idx >= nb_channels) {
366  av_get_channel_layout_string(layout_name, sizeof(layout_name),
367  0, inlink->channel_layout);
368  if (m->in_channel) {
369  channel_name = av_get_channel_name(m->in_channel);
370  av_log(ctx, AV_LOG_ERROR,
371  "input channel '%s' not available from input layout '%s'\n",
372  channel_name, layout_name);
373  } else {
374  av_log(ctx, AV_LOG_ERROR,
375  "input channel #%d not available from input layout '%s'\n",
376  m->in_channel_idx, layout_name);
377  }
378  err = AVERROR(EINVAL);
379  }
380  }
381 
382  return err;
383 }
384 
386  {
387  .name = "default",
388  .type = AVMEDIA_TYPE_AUDIO,
389  .filter_frame = channelmap_filter_frame,
390  .config_props = channelmap_config_input
391  },
392  { NULL }
393 };
394 
396  {
397  .name = "default",
398  .type = AVMEDIA_TYPE_AUDIO
399  },
400  { NULL }
401 };
402 
404  .name = "channelmap",
405  .description = NULL_IF_CONFIG_SMALL("Remap audio channels."),
406  .init = channelmap_init,
407  .query_formats = channelmap_query_formats,
408  .priv_size = sizeof(ChannelMapContext),
409  .priv_class = &channelmap_class,
410 
411  .inputs = avfilter_af_channelmap_inputs,
412  .outputs = avfilter_af_channelmap_outputs,
413 };