LCOV - code coverage report
Current view: top level - libavfilter - af_channelmap.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 105 205 51.2 %
Date: 2017-12-11 04:34:20 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2012 Google, Inc.
       3             :  *
       4             :  * This file is part of FFmpeg.
       5             :  *
       6             :  * FFmpeg 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             :  * FFmpeg 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 FFmpeg; if not, write to the Free Software
      18             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
      19             :  */
      20             : 
      21             : /**
      22             :  * @file
      23             :  * audio channel mapping filter
      24             :  */
      25             : 
      26             : #include <ctype.h>
      27             : 
      28             : #include "libavutil/avstring.h"
      29             : #include "libavutil/channel_layout.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;
      43             :     int in_channel_idx;
      44             :     int out_channel_idx;
      45             : };
      46             : 
      47             : enum MappingMode {
      48             :     MAP_NONE,
      49             :     MAP_ONE_INT,
      50             :     MAP_ONE_STR,
      51             :     MAP_PAIR_INT_INT,
      52             :     MAP_PAIR_INT_STR,
      53             :     MAP_PAIR_STR_INT,
      54             :     MAP_PAIR_STR_STR
      55             : };
      56             : 
      57             : #define MAX_CH 64
      58             : typedef struct ChannelMapContext {
      59             :     const AVClass *class;
      60             :     char *mapping_str;
      61             :     char *channel_layout_str;
      62             :     uint64_t output_layout;
      63             :     struct ChannelMap map[MAX_CH];
      64             :     int nch;
      65             :     enum MappingMode mode;
      66             : } ChannelMapContext;
      67             : 
      68             : #define OFFSET(x) offsetof(ChannelMapContext, x)
      69             : #define A AV_OPT_FLAG_AUDIO_PARAM
      70             : #define F AV_OPT_FLAG_FILTERING_PARAM
      71             : static const AVOption channelmap_options[] = {
      72             :     { "map", "A comma-separated list of input channel numbers in output order.",
      73             :           OFFSET(mapping_str),        AV_OPT_TYPE_STRING, .flags = A|F },
      74             :     { "channel_layout", "Output channel layout.",
      75             :           OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, .flags = A|F },
      76             :     { NULL }
      77             : };
      78             : 
      79             : AVFILTER_DEFINE_CLASS(channelmap);
      80             : 
      81          16 : static char* split(char *message, char delim) {
      82          16 :     char *next = strchr(message, delim);
      83          16 :     if (next)
      84          12 :       *next++ = '\0';
      85          16 :     return next;
      86             : }
      87             : 
      88          12 : static int get_channel_idx(char **map, int *ch, char delim, int max_ch)
      89             : {
      90             :     char *next;
      91             :     int len;
      92          12 :     int n = 0;
      93          12 :     if (!*map)
      94           0 :         return AVERROR(EINVAL);
      95          12 :     next = split(*map, delim);
      96          12 :     if (!next && delim == '-')
      97           0 :         return AVERROR(EINVAL);
      98          12 :     len = strlen(*map);
      99          12 :     sscanf(*map, "%d%n", ch, &n);
     100          12 :     if (n != len)
     101           0 :         return AVERROR(EINVAL);
     102          12 :     if (*ch < 0 || *ch > max_ch)
     103           0 :         return AVERROR(EINVAL);
     104          12 :     *map = next;
     105          12 :     return 0;
     106             : }
     107             : 
     108           4 : static int get_channel(char **map, uint64_t *ch, char delim)
     109             : {
     110           4 :     char *next = split(*map, delim);
     111           4 :     if (!next && delim == '-')
     112           0 :         return AVERROR(EINVAL);
     113           4 :     *ch = av_get_channel_layout(*map);
     114           4 :     if (av_get_channel_layout_nb_channels(*ch) != 1)
     115           0 :         return AVERROR(EINVAL);
     116           4 :     *map = next;
     117           4 :     return 0;
     118             : }
     119             : 
     120           4 : static av_cold int channelmap_init(AVFilterContext *ctx)
     121             : {
     122           4 :     ChannelMapContext *s = ctx->priv;
     123           4 :     char *mapping, separator = '|';
     124           4 :     int map_entries = 0;
     125             :     char buf[256];
     126             :     enum MappingMode mode;
     127           4 :     uint64_t out_ch_mask = 0;
     128             :     int i;
     129             : 
     130           4 :     mapping = s->mapping_str;
     131             : 
     132           4 :     if (!mapping) {
     133           0 :         mode = MAP_NONE;
     134             :     } else {
     135           4 :         char *dash = strchr(mapping, '-');
     136           4 :         if (!dash) {  // short mapping
     137           4 :             if (av_isdigit(*mapping))
     138           2 :                 mode = MAP_ONE_INT;
     139             :             else
     140           2 :                 mode = MAP_ONE_STR;
     141           0 :         } else if (av_isdigit(*mapping)) {
     142           0 :             if (av_isdigit(*(dash+1)))
     143           0 :                 mode = MAP_PAIR_INT_INT;
     144             :             else
     145           0 :                 mode = MAP_PAIR_INT_STR;
     146             :         } else {
     147           0 :             if (av_isdigit(*(dash+1)))
     148           0 :                 mode = MAP_PAIR_STR_INT;
     149             :             else
     150           0 :                 mode = MAP_PAIR_STR_STR;
     151             :         }
     152             :     }
     153             : 
     154           4 :     if (mode != MAP_NONE) {
     155           4 :         char *sep = mapping;
     156           4 :         map_entries = 1;
     157          20 :         while ((sep = strchr(sep, separator))) {
     158          12 :             if (*++sep)  // Allow trailing comma
     159          12 :                 map_entries++;
     160             :         }
     161             :     }
     162             : 
     163           4 :     if (map_entries > MAX_CH) {
     164           0 :         av_log(ctx, AV_LOG_ERROR, "Too many channels mapped: '%d'.\n", map_entries);
     165           0 :         return AVERROR(EINVAL);
     166             :     }
     167             : 
     168          40 :     for (i = 0; i < map_entries; i++) {
     169          16 :         int in_ch_idx = -1, out_ch_idx = -1;
     170          16 :         uint64_t in_ch = 0, out_ch = 0;
     171             :         static const char err[] = "Failed to parse channel map\n";
     172          16 :         switch (mode) {
     173          12 :         case MAP_ONE_INT:
     174          12 :             if (get_channel_idx(&mapping, &in_ch_idx, separator, MAX_CH) < 0) {
     175           0 :                 av_log(ctx, AV_LOG_ERROR, err);
     176           0 :                 return AVERROR(EINVAL);
     177             :             }
     178          12 :             s->map[i].in_channel_idx  = in_ch_idx;
     179          12 :             s->map[i].out_channel_idx = i;
     180          12 :             break;
     181           4 :         case MAP_ONE_STR:
     182           4 :             if (get_channel(&mapping, &in_ch, separator) < 0) {
     183           0 :                 av_log(ctx, AV_LOG_ERROR, err);
     184           0 :                 return AVERROR(EINVAL);
     185             :             }
     186           4 :             s->map[i].in_channel      = in_ch;
     187           4 :             s->map[i].out_channel_idx = i;
     188           4 :             break;
     189           0 :         case MAP_PAIR_INT_INT:
     190           0 :             if (get_channel_idx(&mapping, &in_ch_idx, '-', MAX_CH) < 0 ||
     191           0 :                 get_channel_idx(&mapping, &out_ch_idx, separator, MAX_CH) < 0) {
     192           0 :                 av_log(ctx, AV_LOG_ERROR, err);
     193           0 :                 return AVERROR(EINVAL);
     194             :             }
     195           0 :             s->map[i].in_channel_idx  = in_ch_idx;
     196           0 :             s->map[i].out_channel_idx = out_ch_idx;
     197           0 :             break;
     198           0 :         case MAP_PAIR_INT_STR:
     199           0 :             if (get_channel_idx(&mapping, &in_ch_idx, '-', MAX_CH) < 0 ||
     200           0 :                 get_channel(&mapping, &out_ch, separator) < 0 ||
     201           0 :                 out_ch & out_ch_mask) {
     202           0 :                 av_log(ctx, AV_LOG_ERROR, err);
     203           0 :                 return AVERROR(EINVAL);
     204             :             }
     205           0 :             s->map[i].in_channel_idx  = in_ch_idx;
     206           0 :             s->map[i].out_channel     = out_ch;
     207           0 :             out_ch_mask |= out_ch;
     208           0 :             break;
     209           0 :         case MAP_PAIR_STR_INT:
     210           0 :             if (get_channel(&mapping, &in_ch, '-') < 0 ||
     211           0 :                 get_channel_idx(&mapping, &out_ch_idx, separator, MAX_CH) < 0) {
     212           0 :                 av_log(ctx, AV_LOG_ERROR, err);
     213           0 :                 return AVERROR(EINVAL);
     214             :             }
     215           0 :             s->map[i].in_channel      = in_ch;
     216           0 :             s->map[i].out_channel_idx = out_ch_idx;
     217           0 :             break;
     218           0 :         case MAP_PAIR_STR_STR:
     219           0 :             if (get_channel(&mapping, &in_ch, '-') < 0 ||
     220           0 :                 get_channel(&mapping, &out_ch, separator) < 0 ||
     221           0 :                 out_ch & out_ch_mask) {
     222           0 :                 av_log(ctx, AV_LOG_ERROR, err);
     223           0 :                 return AVERROR(EINVAL);
     224             :             }
     225           0 :             s->map[i].in_channel = in_ch;
     226           0 :             s->map[i].out_channel = out_ch;
     227           0 :             out_ch_mask |= out_ch;
     228           0 :             break;
     229             :         }
     230             :     }
     231           4 :     s->mode          = mode;
     232           4 :     s->nch           = map_entries;
     233           8 :     s->output_layout = out_ch_mask ? out_ch_mask :
     234           4 :                        av_get_default_channel_layout(map_entries);
     235             : 
     236           4 :     if (s->channel_layout_str) {
     237             :         uint64_t fmt;
     238           4 :         if ((fmt = av_get_channel_layout(s->channel_layout_str)) == 0) {
     239           0 :             av_log(ctx, AV_LOG_ERROR, "Error parsing channel layout: '%s'.\n",
     240             :                    s->channel_layout_str);
     241           0 :             return AVERROR(EINVAL);
     242             :         }
     243           4 :         if (mode == MAP_NONE) {
     244             :             int i;
     245           0 :             s->nch = av_get_channel_layout_nb_channels(fmt);
     246           0 :             for (i = 0; i < s->nch; i++) {
     247           0 :                 s->map[i].in_channel_idx  = i;
     248           0 :                 s->map[i].out_channel_idx = i;
     249             :             }
     250           4 :         } else if (out_ch_mask && out_ch_mask != fmt) {
     251           0 :             av_get_channel_layout_string(buf, sizeof(buf), 0, out_ch_mask);
     252           0 :             av_log(ctx, AV_LOG_ERROR,
     253             :                    "Output channel layout '%s' does not match the list of channel mapped: '%s'.\n",
     254             :                    s->channel_layout_str, buf);
     255           0 :             return AVERROR(EINVAL);
     256           4 :         } else if (s->nch != av_get_channel_layout_nb_channels(fmt)) {
     257           0 :             av_log(ctx, AV_LOG_ERROR,
     258             :                    "Output channel layout %s does not match the number of channels mapped %d.\n",
     259             :                    s->channel_layout_str, s->nch);
     260           0 :             return AVERROR(EINVAL);
     261             :         }
     262           4 :         s->output_layout = fmt;
     263             :     }
     264           4 :     if (!s->output_layout) {
     265           0 :         av_log(ctx, AV_LOG_ERROR, "Output channel layout is not set and "
     266             :                "cannot be guessed from the maps.\n");
     267           0 :         return AVERROR(EINVAL);
     268             :     }
     269             : 
     270           4 :     if (mode == MAP_PAIR_INT_STR || mode == MAP_PAIR_STR_STR) {
     271           0 :         for (i = 0; i < s->nch; i++) {
     272           0 :             s->map[i].out_channel_idx = av_get_channel_layout_channel_index(
     273             :                 s->output_layout, s->map[i].out_channel);
     274             :         }
     275             :     }
     276             : 
     277           4 :     return 0;
     278             : }
     279             : 
     280           2 : static int channelmap_query_formats(AVFilterContext *ctx)
     281             : {
     282           2 :     ChannelMapContext *s = ctx->priv;
     283             :     AVFilterChannelLayouts *layouts;
     284           2 :     AVFilterChannelLayouts *channel_layouts = NULL;
     285             :     int ret;
     286             : 
     287           2 :     layouts = ff_all_channel_counts();
     288           2 :     if (!layouts) {
     289           0 :         ret = AVERROR(ENOMEM);
     290           0 :         goto fail;
     291             :     }
     292           4 :     if ((ret = ff_add_channel_layout     (&channel_layouts, s->output_layout                    )) < 0 ||
     293           4 :         (ret = ff_set_common_formats     (ctx             , ff_planar_sample_fmts()             )) < 0 ||
     294           4 :         (ret = ff_set_common_samplerates (ctx             , ff_all_samplerates()                )) < 0 ||
     295           4 :         (ret = ff_channel_layouts_ref    (layouts         , &ctx->inputs[0]->out_channel_layouts)) < 0 ||
     296           2 :         (ret = ff_channel_layouts_ref    (channel_layouts , &ctx->outputs[0]->in_channel_layouts)) < 0)
     297             :             goto fail;
     298             : 
     299           2 :     return 0;
     300           0 : fail:
     301           0 :     if (layouts)
     302           0 :         av_freep(&layouts->channel_layouts);
     303           0 :     av_freep(&layouts);
     304           0 :     return ret;
     305             : }
     306             : 
     307        1035 : static int channelmap_filter_frame(AVFilterLink *inlink, AVFrame *buf)
     308             : {
     309        1035 :     AVFilterContext  *ctx = inlink->dst;
     310        1035 :     AVFilterLink *outlink = ctx->outputs[0];
     311        1035 :     const ChannelMapContext *s = ctx->priv;
     312        1035 :     const int nch_in = inlink->channels;
     313        1035 :     const int nch_out = s->nch;
     314             :     int ch;
     315             :     uint8_t *source_planes[MAX_CH];
     316             : 
     317        1035 :     memcpy(source_planes, buf->extended_data,
     318             :            nch_in * sizeof(source_planes[0]));
     319             : 
     320        1035 :     if (nch_out > nch_in) {
     321           0 :         if (nch_out > FF_ARRAY_ELEMS(buf->data)) {
     322           0 :             uint8_t **new_extended_data =
     323           0 :                 av_mallocz_array(nch_out, sizeof(*buf->extended_data));
     324           0 :             if (!new_extended_data) {
     325           0 :                 av_frame_free(&buf);
     326           0 :                 return AVERROR(ENOMEM);
     327             :             }
     328           0 :             if (buf->extended_data == buf->data) {
     329           0 :                 buf->extended_data = new_extended_data;
     330             :             } else {
     331           0 :                 av_free(buf->extended_data);
     332           0 :                 buf->extended_data = new_extended_data;
     333             :             }
     334           0 :         } else if (buf->extended_data != buf->data) {
     335           0 :             av_free(buf->extended_data);
     336           0 :             buf->extended_data = buf->data;
     337             :         }
     338             :     }
     339             : 
     340        6209 :     for (ch = 0; ch < nch_out; ch++) {
     341       10348 :         buf->extended_data[s->map[ch].out_channel_idx] =
     342       10348 :             source_planes[s->map[ch].in_channel_idx];
     343             :     }
     344             : 
     345        1035 :     if (buf->data != buf->extended_data)
     346           0 :         memcpy(buf->data, buf->extended_data,
     347           0 :            FFMIN(FF_ARRAY_ELEMS(buf->data), nch_out) * sizeof(buf->data[0]));
     348             : 
     349        1035 :     buf->channel_layout = outlink->channel_layout;
     350        1035 :     buf->channels       = outlink->channels;
     351             : 
     352        1035 :     return ff_filter_frame(outlink, buf);
     353             : }
     354             : 
     355           2 : static int channelmap_config_input(AVFilterLink *inlink)
     356             : {
     357           2 :     AVFilterContext *ctx = inlink->dst;
     358           2 :     ChannelMapContext *s = ctx->priv;
     359           2 :     int nb_channels = inlink->channels;
     360           2 :     int i, err = 0;
     361             :     const char *channel_name;
     362             :     char layout_name[256];
     363             : 
     364          10 :     for (i = 0; i < s->nch; i++) {
     365           8 :         struct ChannelMap *m = &s->map[i];
     366             : 
     367           8 :         if (s->mode == MAP_PAIR_STR_INT || s->mode == MAP_PAIR_STR_STR) {
     368           0 :             m->in_channel_idx = av_get_channel_layout_channel_index(
     369             :                 inlink->channel_layout, m->in_channel);
     370             :         }
     371             : 
     372           8 :         if (m->in_channel_idx < 0 || m->in_channel_idx >= nb_channels) {
     373           0 :             av_get_channel_layout_string(layout_name, sizeof(layout_name),
     374             :                                          nb_channels, inlink->channel_layout);
     375           0 :             if (m->in_channel) {
     376           0 :                 channel_name = av_get_channel_name(m->in_channel);
     377           0 :                 av_log(ctx, AV_LOG_ERROR,
     378             :                        "input channel '%s' not available from input layout '%s'\n",
     379             :                        channel_name, layout_name);
     380             :             } else {
     381           0 :                 av_log(ctx, AV_LOG_ERROR,
     382             :                        "input channel #%d not available from input layout '%s'\n",
     383             :                        m->in_channel_idx, layout_name);
     384             :             }
     385           0 :             err = AVERROR(EINVAL);
     386             :         }
     387             :     }
     388             : 
     389           2 :     return err;
     390             : }
     391             : 
     392             : static const AVFilterPad avfilter_af_channelmap_inputs[] = {
     393             :     {
     394             :         .name           = "default",
     395             :         .type           = AVMEDIA_TYPE_AUDIO,
     396             :         .filter_frame   = channelmap_filter_frame,
     397             :         .config_props   = channelmap_config_input,
     398             :         .needs_writable = 1,
     399             :     },
     400             :     { NULL }
     401             : };
     402             : 
     403             : static const AVFilterPad avfilter_af_channelmap_outputs[] = {
     404             :     {
     405             :         .name = "default",
     406             :         .type = AVMEDIA_TYPE_AUDIO
     407             :     },
     408             :     { NULL }
     409             : };
     410             : 
     411             : AVFilter ff_af_channelmap = {
     412             :     .name          = "channelmap",
     413             :     .description   = NULL_IF_CONFIG_SMALL("Remap audio channels."),
     414             :     .init          = channelmap_init,
     415             :     .query_formats = channelmap_query_formats,
     416             :     .priv_size     = sizeof(ChannelMapContext),
     417             :     .priv_class    = &channelmap_class,
     418             :     .inputs        = avfilter_af_channelmap_inputs,
     419             :     .outputs       = avfilter_af_channelmap_outputs,
     420             : };

Generated by: LCOV version 1.13