LCOV - code coverage report
Current view: top level - src/libavfilter - af_channelmap.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 106 208 51.0 %
Date: 2017-07-21 15:41: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             : #if FF_API_OLD_FILTER_OPTS
     153           4 :         if (strchr(mapping, ',')) {
     154           0 :             av_log(ctx, AV_LOG_WARNING, "This syntax is deprecated, use "
     155             :                    "'|' to separate the mappings.\n");
     156           0 :             separator = ',';
     157             :         }
     158             : #endif
     159             :     }
     160             : 
     161           4 :     if (mode != MAP_NONE) {
     162           4 :         char *sep = mapping;
     163           4 :         map_entries = 1;
     164          20 :         while ((sep = strchr(sep, separator))) {
     165          12 :             if (*++sep)  // Allow trailing comma
     166          12 :                 map_entries++;
     167             :         }
     168             :     }
     169             : 
     170           4 :     if (map_entries > MAX_CH) {
     171           0 :         av_log(ctx, AV_LOG_ERROR, "Too many channels mapped: '%d'.\n", map_entries);
     172           0 :         return AVERROR(EINVAL);
     173             :     }
     174             : 
     175          40 :     for (i = 0; i < map_entries; i++) {
     176          16 :         int in_ch_idx = -1, out_ch_idx = -1;
     177          16 :         uint64_t in_ch = 0, out_ch = 0;
     178             :         static const char err[] = "Failed to parse channel map\n";
     179          16 :         switch (mode) {
     180          12 :         case MAP_ONE_INT:
     181          12 :             if (get_channel_idx(&mapping, &in_ch_idx, separator, MAX_CH) < 0) {
     182           0 :                 av_log(ctx, AV_LOG_ERROR, err);
     183           0 :                 return AVERROR(EINVAL);
     184             :             }
     185          12 :             s->map[i].in_channel_idx  = in_ch_idx;
     186          12 :             s->map[i].out_channel_idx = i;
     187          12 :             break;
     188           4 :         case MAP_ONE_STR:
     189           4 :             if (get_channel(&mapping, &in_ch, separator) < 0) {
     190           0 :                 av_log(ctx, AV_LOG_ERROR, err);
     191           0 :                 return AVERROR(EINVAL);
     192             :             }
     193           4 :             s->map[i].in_channel      = in_ch;
     194           4 :             s->map[i].out_channel_idx = i;
     195           4 :             break;
     196           0 :         case MAP_PAIR_INT_INT:
     197           0 :             if (get_channel_idx(&mapping, &in_ch_idx, '-', MAX_CH) < 0 ||
     198           0 :                 get_channel_idx(&mapping, &out_ch_idx, separator, MAX_CH) < 0) {
     199           0 :                 av_log(ctx, AV_LOG_ERROR, err);
     200           0 :                 return AVERROR(EINVAL);
     201             :             }
     202           0 :             s->map[i].in_channel_idx  = in_ch_idx;
     203           0 :             s->map[i].out_channel_idx = out_ch_idx;
     204           0 :             break;
     205           0 :         case MAP_PAIR_INT_STR:
     206           0 :             if (get_channel_idx(&mapping, &in_ch_idx, '-', MAX_CH) < 0 ||
     207           0 :                 get_channel(&mapping, &out_ch, separator) < 0 ||
     208           0 :                 out_ch & out_ch_mask) {
     209           0 :                 av_log(ctx, AV_LOG_ERROR, err);
     210           0 :                 return AVERROR(EINVAL);
     211             :             }
     212           0 :             s->map[i].in_channel_idx  = in_ch_idx;
     213           0 :             s->map[i].out_channel     = out_ch;
     214           0 :             out_ch_mask |= out_ch;
     215           0 :             break;
     216           0 :         case MAP_PAIR_STR_INT:
     217           0 :             if (get_channel(&mapping, &in_ch, '-') < 0 ||
     218           0 :                 get_channel_idx(&mapping, &out_ch_idx, separator, MAX_CH) < 0) {
     219           0 :                 av_log(ctx, AV_LOG_ERROR, err);
     220           0 :                 return AVERROR(EINVAL);
     221             :             }
     222           0 :             s->map[i].in_channel      = in_ch;
     223           0 :             s->map[i].out_channel_idx = out_ch_idx;
     224           0 :             break;
     225           0 :         case MAP_PAIR_STR_STR:
     226           0 :             if (get_channel(&mapping, &in_ch, '-') < 0 ||
     227           0 :                 get_channel(&mapping, &out_ch, separator) < 0 ||
     228           0 :                 out_ch & out_ch_mask) {
     229           0 :                 av_log(ctx, AV_LOG_ERROR, err);
     230           0 :                 return AVERROR(EINVAL);
     231             :             }
     232           0 :             s->map[i].in_channel = in_ch;
     233           0 :             s->map[i].out_channel = out_ch;
     234           0 :             out_ch_mask |= out_ch;
     235           0 :             break;
     236             :         }
     237             :     }
     238           4 :     s->mode          = mode;
     239           4 :     s->nch           = map_entries;
     240           8 :     s->output_layout = out_ch_mask ? out_ch_mask :
     241           4 :                        av_get_default_channel_layout(map_entries);
     242             : 
     243           4 :     if (s->channel_layout_str) {
     244             :         uint64_t fmt;
     245           4 :         if ((fmt = av_get_channel_layout(s->channel_layout_str)) == 0) {
     246           0 :             av_log(ctx, AV_LOG_ERROR, "Error parsing channel layout: '%s'.\n",
     247             :                    s->channel_layout_str);
     248           0 :             return AVERROR(EINVAL);
     249             :         }
     250           4 :         if (mode == MAP_NONE) {
     251             :             int i;
     252           0 :             s->nch = av_get_channel_layout_nb_channels(fmt);
     253           0 :             for (i = 0; i < s->nch; i++) {
     254           0 :                 s->map[i].in_channel_idx  = i;
     255           0 :                 s->map[i].out_channel_idx = i;
     256             :             }
     257           4 :         } else if (out_ch_mask && out_ch_mask != fmt) {
     258           0 :             av_get_channel_layout_string(buf, sizeof(buf), 0, out_ch_mask);
     259           0 :             av_log(ctx, AV_LOG_ERROR,
     260             :                    "Output channel layout '%s' does not match the list of channel mapped: '%s'.\n",
     261             :                    s->channel_layout_str, buf);
     262           0 :             return AVERROR(EINVAL);
     263           4 :         } else if (s->nch != av_get_channel_layout_nb_channels(fmt)) {
     264           0 :             av_log(ctx, AV_LOG_ERROR,
     265             :                    "Output channel layout %s does not match the number of channels mapped %d.\n",
     266             :                    s->channel_layout_str, s->nch);
     267           0 :             return AVERROR(EINVAL);
     268             :         }
     269           4 :         s->output_layout = fmt;
     270             :     }
     271           4 :     if (!s->output_layout) {
     272           0 :         av_log(ctx, AV_LOG_ERROR, "Output channel layout is not set and "
     273             :                "cannot be guessed from the maps.\n");
     274           0 :         return AVERROR(EINVAL);
     275             :     }
     276             : 
     277           4 :     if (mode == MAP_PAIR_INT_STR || mode == MAP_PAIR_STR_STR) {
     278           0 :         for (i = 0; i < s->nch; i++) {
     279           0 :             s->map[i].out_channel_idx = av_get_channel_layout_channel_index(
     280             :                 s->output_layout, s->map[i].out_channel);
     281             :         }
     282             :     }
     283             : 
     284           4 :     return 0;
     285             : }
     286             : 
     287           2 : static int channelmap_query_formats(AVFilterContext *ctx)
     288             : {
     289           2 :     ChannelMapContext *s = ctx->priv;
     290             :     AVFilterChannelLayouts *layouts;
     291           2 :     AVFilterChannelLayouts *channel_layouts = NULL;
     292             :     int ret;
     293             : 
     294           2 :     layouts = ff_all_channel_counts();
     295           2 :     if (!layouts) {
     296           0 :         ret = AVERROR(ENOMEM);
     297           0 :         goto fail;
     298             :     }
     299           4 :     if ((ret = ff_add_channel_layout     (&channel_layouts, s->output_layout                    )) < 0 ||
     300           4 :         (ret = ff_set_common_formats     (ctx             , ff_planar_sample_fmts()             )) < 0 ||
     301           4 :         (ret = ff_set_common_samplerates (ctx             , ff_all_samplerates()                )) < 0 ||
     302           4 :         (ret = ff_channel_layouts_ref    (layouts         , &ctx->inputs[0]->out_channel_layouts)) < 0 ||
     303           2 :         (ret = ff_channel_layouts_ref    (channel_layouts , &ctx->outputs[0]->in_channel_layouts)) < 0)
     304             :             goto fail;
     305             : 
     306           2 :     return 0;
     307           0 : fail:
     308           0 :     if (layouts)
     309           0 :         av_freep(&layouts->channel_layouts);
     310           0 :     av_freep(&layouts);
     311           0 :     return ret;
     312             : }
     313             : 
     314        1035 : static int channelmap_filter_frame(AVFilterLink *inlink, AVFrame *buf)
     315             : {
     316        1035 :     AVFilterContext  *ctx = inlink->dst;
     317        1035 :     AVFilterLink *outlink = ctx->outputs[0];
     318        1035 :     const ChannelMapContext *s = ctx->priv;
     319        1035 :     const int nch_in = inlink->channels;
     320        1035 :     const int nch_out = s->nch;
     321             :     int ch;
     322             :     uint8_t *source_planes[MAX_CH];
     323             : 
     324        1035 :     memcpy(source_planes, buf->extended_data,
     325             :            nch_in * sizeof(source_planes[0]));
     326             : 
     327        1035 :     if (nch_out > nch_in) {
     328           0 :         if (nch_out > FF_ARRAY_ELEMS(buf->data)) {
     329           0 :             uint8_t **new_extended_data =
     330           0 :                 av_mallocz_array(nch_out, sizeof(*buf->extended_data));
     331           0 :             if (!new_extended_data) {
     332           0 :                 av_frame_free(&buf);
     333           0 :                 return AVERROR(ENOMEM);
     334             :             }
     335           0 :             if (buf->extended_data == buf->data) {
     336           0 :                 buf->extended_data = new_extended_data;
     337             :             } else {
     338           0 :                 av_free(buf->extended_data);
     339           0 :                 buf->extended_data = new_extended_data;
     340             :             }
     341           0 :         } else if (buf->extended_data != buf->data) {
     342           0 :             av_free(buf->extended_data);
     343           0 :             buf->extended_data = buf->data;
     344             :         }
     345             :     }
     346             : 
     347        6209 :     for (ch = 0; ch < nch_out; ch++) {
     348       10348 :         buf->extended_data[s->map[ch].out_channel_idx] =
     349       10348 :             source_planes[s->map[ch].in_channel_idx];
     350             :     }
     351             : 
     352        1035 :     if (buf->data != buf->extended_data)
     353           0 :         memcpy(buf->data, buf->extended_data,
     354           0 :            FFMIN(FF_ARRAY_ELEMS(buf->data), nch_out) * sizeof(buf->data[0]));
     355             : 
     356        1035 :     buf->channel_layout = outlink->channel_layout;
     357        1035 :     buf->channels       = outlink->channels;
     358             : 
     359        1035 :     return ff_filter_frame(outlink, buf);
     360             : }
     361             : 
     362           2 : static int channelmap_config_input(AVFilterLink *inlink)
     363             : {
     364           2 :     AVFilterContext *ctx = inlink->dst;
     365           2 :     ChannelMapContext *s = ctx->priv;
     366           2 :     int nb_channels = inlink->channels;
     367           2 :     int i, err = 0;
     368             :     const char *channel_name;
     369             :     char layout_name[256];
     370             : 
     371          10 :     for (i = 0; i < s->nch; i++) {
     372           8 :         struct ChannelMap *m = &s->map[i];
     373             : 
     374           8 :         if (s->mode == MAP_PAIR_STR_INT || s->mode == MAP_PAIR_STR_STR) {
     375           0 :             m->in_channel_idx = av_get_channel_layout_channel_index(
     376             :                 inlink->channel_layout, m->in_channel);
     377             :         }
     378             : 
     379           8 :         if (m->in_channel_idx < 0 || m->in_channel_idx >= nb_channels) {
     380           0 :             av_get_channel_layout_string(layout_name, sizeof(layout_name),
     381             :                                          nb_channels, inlink->channel_layout);
     382           0 :             if (m->in_channel) {
     383           0 :                 channel_name = av_get_channel_name(m->in_channel);
     384           0 :                 av_log(ctx, AV_LOG_ERROR,
     385             :                        "input channel '%s' not available from input layout '%s'\n",
     386             :                        channel_name, layout_name);
     387             :             } else {
     388           0 :                 av_log(ctx, AV_LOG_ERROR,
     389             :                        "input channel #%d not available from input layout '%s'\n",
     390             :                        m->in_channel_idx, layout_name);
     391             :             }
     392           0 :             err = AVERROR(EINVAL);
     393             :         }
     394             :     }
     395             : 
     396           2 :     return err;
     397             : }
     398             : 
     399             : static const AVFilterPad avfilter_af_channelmap_inputs[] = {
     400             :     {
     401             :         .name           = "default",
     402             :         .type           = AVMEDIA_TYPE_AUDIO,
     403             :         .filter_frame   = channelmap_filter_frame,
     404             :         .config_props   = channelmap_config_input,
     405             :         .needs_writable = 1,
     406             :     },
     407             :     { NULL }
     408             : };
     409             : 
     410             : static const AVFilterPad avfilter_af_channelmap_outputs[] = {
     411             :     {
     412             :         .name = "default",
     413             :         .type = AVMEDIA_TYPE_AUDIO
     414             :     },
     415             :     { NULL }
     416             : };
     417             : 
     418             : AVFilter ff_af_channelmap = {
     419             :     .name          = "channelmap",
     420             :     .description   = NULL_IF_CONFIG_SMALL("Remap audio channels."),
     421             :     .init          = channelmap_init,
     422             :     .query_formats = channelmap_query_formats,
     423             :     .priv_size     = sizeof(ChannelMapContext),
     424             :     .priv_class    = &channelmap_class,
     425             :     .inputs        = avfilter_af_channelmap_inputs,
     426             :     .outputs       = avfilter_af_channelmap_outputs,
     427             : };

Generated by: LCOV version 1.13