LCOV - code coverage report
Current view: top level - src/libavfilter - aeval.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 137 177 77.4 %
Date: 2017-01-22 19:06:18 Functions: 10 10 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2011 Stefano Sabatini
       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             :  * eval audio source
      24             :  */
      25             : 
      26             : #include "libavutil/avassert.h"
      27             : #include "libavutil/avstring.h"
      28             : #include "libavutil/channel_layout.h"
      29             : #include "libavutil/eval.h"
      30             : #include "libavutil/opt.h"
      31             : #include "libavutil/parseutils.h"
      32             : #include "avfilter.h"
      33             : #include "audio.h"
      34             : #include "internal.h"
      35             : 
      36             : static const char * const var_names[] = {
      37             :     "ch",           ///< the value of the current channel
      38             :     "n",            ///< number of frame
      39             :     "nb_in_channels",
      40             :     "nb_out_channels",
      41             :     "t",            ///< timestamp expressed in seconds
      42             :     "s",            ///< sample rate
      43             :     NULL
      44             : };
      45             : 
      46             : enum var_name {
      47             :     VAR_CH,
      48             :     VAR_N,
      49             :     VAR_NB_IN_CHANNELS,
      50             :     VAR_NB_OUT_CHANNELS,
      51             :     VAR_T,
      52             :     VAR_S,
      53             :     VAR_VARS_NB
      54             : };
      55             : 
      56             : typedef struct {
      57             :     const AVClass *class;
      58             :     char *sample_rate_str;
      59             :     int sample_rate;
      60             :     int64_t chlayout;
      61             :     char *chlayout_str;
      62             :     int nb_channels;            ///< number of output channels
      63             :     int nb_in_channels;         ///< number of input channels
      64             :     int same_chlayout;          ///< set output as input channel layout
      65             :     int64_t pts;
      66             :     AVExpr **expr;
      67             :     char *exprs;
      68             :     int nb_samples;             ///< number of samples per requested frame
      69             :     int64_t duration;
      70             :     uint64_t n;
      71             :     double var_values[VAR_VARS_NB];
      72             :     double *channel_values;
      73             :     int64_t out_channel_layout;
      74             : } EvalContext;
      75             : 
      76      264600 : static double val(void *priv, double ch)
      77             : {
      78      264600 :     EvalContext *eval = priv;
      79      264600 :     return eval->channel_values[FFMIN((int)ch, eval->nb_in_channels-1)];
      80             : }
      81             : 
      82             : static double (* const aeval_func1[])(void *, double) = { val, NULL };
      83             : static const char * const aeval_func1_names[] = { "val", NULL };
      84             : 
      85             : #define OFFSET(x) offsetof(EvalContext, x)
      86             : #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
      87             : 
      88             : static const AVOption aevalsrc_options[]= {
      89             :     { "exprs",       "set the '|'-separated list of channels expressions", OFFSET(exprs), AV_OPT_TYPE_STRING, {.str = NULL}, .flags = FLAGS },
      90             :     { "nb_samples",  "set the number of samples per requested frame", OFFSET(nb_samples),      AV_OPT_TYPE_INT,    {.i64 = 1024},    0,        INT_MAX, FLAGS },
      91             :     { "n",           "set the number of samples per requested frame", OFFSET(nb_samples),      AV_OPT_TYPE_INT,    {.i64 = 1024},    0,        INT_MAX, FLAGS },
      92             :     { "sample_rate", "set the sample rate",                           OFFSET(sample_rate_str), AV_OPT_TYPE_STRING, {.str = "44100"}, CHAR_MIN, CHAR_MAX, FLAGS },
      93             :     { "s",           "set the sample rate",                           OFFSET(sample_rate_str), AV_OPT_TYPE_STRING, {.str = "44100"}, CHAR_MIN, CHAR_MAX, FLAGS },
      94             :     { "duration",    "set audio duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = -1}, -1, INT64_MAX, FLAGS },
      95             :     { "d",           "set audio duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = -1}, -1, INT64_MAX, FLAGS },
      96             :     { "channel_layout", "set channel layout", OFFSET(chlayout_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
      97             :     { "c",              "set channel layout", OFFSET(chlayout_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
      98             :     { NULL }
      99             : };
     100             : 
     101             : AVFILTER_DEFINE_CLASS(aevalsrc);
     102             : 
     103           5 : static int parse_channel_expressions(AVFilterContext *ctx,
     104             :                                      int expected_nb_channels)
     105             : {
     106           5 :     EvalContext *eval = ctx->priv;
     107           5 :     char *args1 = av_strdup(eval->exprs);
     108           5 :     char *expr, *last_expr = NULL, *buf;
     109           5 :     double (* const *func1)(void *, double) = NULL;
     110           5 :     const char * const *func1_names = NULL;
     111           5 :     int i, ret = 0;
     112             : 
     113           5 :     if (!args1)
     114           0 :         return AVERROR(ENOMEM);
     115             : 
     116           5 :     if (!eval->exprs) {
     117           0 :         av_log(ctx, AV_LOG_ERROR, "Channels expressions list is empty\n");
     118           0 :         return AVERROR(EINVAL);
     119             :     }
     120             : 
     121           5 :     if (!strcmp(ctx->filter->name, "aeval")) {
     122           1 :         func1 = aeval_func1;
     123           1 :         func1_names = aeval_func1_names;
     124             :     }
     125             : 
     126             : #define ADD_EXPRESSION(expr_) do {                                      \
     127             :         if (!av_dynarray2_add((void **)&eval->expr, &eval->nb_channels, \
     128             :                               sizeof(*eval->expr), NULL)) {             \
     129             :             ret = AVERROR(ENOMEM);                                      \
     130             :             goto end;                                                   \
     131             :         }                                                               \
     132             :         eval->expr[eval->nb_channels-1] = NULL;                         \
     133             :         ret = av_expr_parse(&eval->expr[eval->nb_channels - 1], expr_,  \
     134             :                             var_names, func1_names, func1,              \
     135             :                             NULL, NULL, 0, ctx);                        \
     136             :         if (ret < 0)                                                    \
     137             :             goto end;                                                   \
     138             :     } while (0)
     139             : 
     140             :     /* reset expressions */
     141           5 :     for (i = 0; i < eval->nb_channels; i++) {
     142           0 :         av_expr_free(eval->expr[i]);
     143           0 :         eval->expr[i] = NULL;
     144             :     }
     145           5 :     av_freep(&eval->expr);
     146           5 :     eval->nb_channels = 0;
     147             : 
     148           5 :     buf = args1;
     149          15 :     while (expr = av_strtok(buf, "|", &buf)) {
     150           5 :         ADD_EXPRESSION(expr);
     151           5 :         last_expr = expr;
     152             :     }
     153             : 
     154           5 :     if (expected_nb_channels > eval->nb_channels)
     155           0 :         for (i = eval->nb_channels; i < expected_nb_channels; i++)
     156           0 :             ADD_EXPRESSION(last_expr);
     157             : 
     158           5 :     if (expected_nb_channels > 0 && eval->nb_channels != expected_nb_channels) {
     159           0 :         av_log(ctx, AV_LOG_ERROR,
     160             :                "Mismatch between the specified number of channel expressions '%d' "
     161             :                "and the number of expected output channels '%d' for the specified channel layout\n",
     162             :                eval->nb_channels, expected_nb_channels);
     163           0 :         ret = AVERROR(EINVAL);
     164           0 :         goto end;
     165             :     }
     166             : 
     167             : end:
     168           5 :     av_free(args1);
     169           5 :     return ret;
     170             : }
     171             : 
     172           5 : static av_cold int init(AVFilterContext *ctx)
     173             : {
     174           5 :     EvalContext *eval = ctx->priv;
     175           5 :     int ret = 0;
     176             : 
     177           5 :     if (eval->chlayout_str) {
     178           0 :         if (!strcmp(eval->chlayout_str, "same") && !strcmp(ctx->filter->name, "aeval")) {
     179           0 :             eval->same_chlayout = 1;
     180             :         } else {
     181           0 :             ret = ff_parse_channel_layout(&eval->chlayout, NULL, eval->chlayout_str, ctx);
     182           0 :             if (ret < 0)
     183           0 :                 return ret;
     184             : 
     185           0 :             ret = parse_channel_expressions(ctx, av_get_channel_layout_nb_channels(eval->chlayout));
     186           0 :             if (ret < 0)
     187           0 :                 return ret;
     188             :         }
     189             :     } else {
     190             :         /* guess channel layout from nb expressions/channels */
     191           5 :         if ((ret = parse_channel_expressions(ctx, -1)) < 0)
     192           0 :             return ret;
     193             : 
     194           5 :         eval->chlayout = av_get_default_channel_layout(eval->nb_channels);
     195           5 :         if (!eval->chlayout && eval->nb_channels <= 0) {
     196           0 :             av_log(ctx, AV_LOG_ERROR, "Invalid number of channels '%d' provided\n",
     197             :                    eval->nb_channels);
     198           0 :             return AVERROR(EINVAL);
     199             :         }
     200             :     }
     201             : 
     202           5 :     if (eval->sample_rate_str)
     203           4 :         if ((ret = ff_parse_sample_rate(&eval->sample_rate, eval->sample_rate_str, ctx)))
     204           0 :             return ret;
     205           5 :     eval->n = 0;
     206             : 
     207           5 :     return ret;
     208             : }
     209             : 
     210           5 : static av_cold void uninit(AVFilterContext *ctx)
     211             : {
     212           5 :     EvalContext *eval = ctx->priv;
     213             :     int i;
     214             : 
     215          10 :     for (i = 0; i < eval->nb_channels; i++) {
     216           5 :         av_expr_free(eval->expr[i]);
     217           5 :         eval->expr[i] = NULL;
     218             :     }
     219           5 :     av_freep(&eval->expr);
     220           5 :     av_freep(&eval->channel_values);
     221           5 : }
     222             : 
     223           4 : static int config_props(AVFilterLink *outlink)
     224             : {
     225           4 :     EvalContext *eval = outlink->src->priv;
     226             :     char buf[128];
     227             : 
     228           4 :     outlink->time_base = (AVRational){1, eval->sample_rate};
     229           4 :     outlink->sample_rate = eval->sample_rate;
     230             : 
     231           4 :     eval->var_values[VAR_S] = eval->sample_rate;
     232           4 :     eval->var_values[VAR_NB_IN_CHANNELS] = NAN;
     233           4 :     eval->var_values[VAR_NB_OUT_CHANNELS] = outlink->channels;
     234             : 
     235           4 :     av_get_channel_layout_string(buf, sizeof(buf), 0, eval->chlayout);
     236             : 
     237           4 :     av_log(outlink->src, AV_LOG_VERBOSE,
     238             :            "sample_rate:%d chlayout:%s duration:%"PRId64"\n",
     239             :            eval->sample_rate, buf, eval->duration);
     240             : 
     241           4 :     return 0;
     242             : }
     243             : 
     244           4 : static int query_formats(AVFilterContext *ctx)
     245             : {
     246           4 :     EvalContext *eval = ctx->priv;
     247             :     static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_DBLP, AV_SAMPLE_FMT_NONE };
     248           4 :     int64_t chlayouts[] = { eval->chlayout ? eval->chlayout : FF_COUNT2LAYOUT(eval->nb_channels) , -1 };
     249           4 :     int sample_rates[] = { eval->sample_rate, -1 };
     250             :     AVFilterFormats *formats;
     251             :     AVFilterChannelLayouts *layouts;
     252             :     int ret;
     253             : 
     254           4 :     formats = ff_make_format_list(sample_fmts);
     255           4 :     if (!formats)
     256           0 :         return AVERROR(ENOMEM);
     257           4 :     ret = ff_set_common_formats (ctx, formats);
     258           4 :     if (ret < 0)
     259           0 :         return ret;
     260             : 
     261           4 :     layouts = avfilter_make_format64_list(chlayouts);
     262           4 :     if (!layouts)
     263           0 :         return AVERROR(ENOMEM);
     264           4 :     ret = ff_set_common_channel_layouts(ctx, layouts);
     265           4 :     if (ret < 0)
     266           0 :         return ret;
     267             : 
     268           4 :     formats = ff_make_format_list(sample_rates);
     269           4 :     if (!formats)
     270           0 :         return AVERROR(ENOMEM);
     271           4 :     return ff_set_common_samplerates(ctx, formats);
     272             : }
     273             : 
     274        2596 : static int request_frame(AVFilterLink *outlink)
     275             : {
     276        2596 :     EvalContext *eval = outlink->src->priv;
     277             :     AVFrame *samplesref;
     278             :     int i, j;
     279        2596 :     int64_t t = av_rescale(eval->n, AV_TIME_BASE, eval->sample_rate);
     280             : 
     281        2596 :     if (eval->duration >= 0 && t >= eval->duration)
     282           4 :         return AVERROR_EOF;
     283             : 
     284        2592 :     samplesref = ff_get_audio_buffer(outlink, eval->nb_samples);
     285        2592 :     if (!samplesref)
     286           0 :         return AVERROR(ENOMEM);
     287             : 
     288             :     /* evaluate expression for each single sample and for each channel */
     289     2656800 :     for (i = 0; i < eval->nb_samples; i++, eval->n++) {
     290     2654208 :         eval->var_values[VAR_N] = eval->n;
     291     2654208 :         eval->var_values[VAR_T] = eval->var_values[VAR_N] * (double)1/eval->sample_rate;
     292             : 
     293     5308416 :         for (j = 0; j < eval->nb_channels; j++) {
     294     5308416 :             *((double *) samplesref->extended_data[j] + i) =
     295     2654208 :                 av_expr_eval(eval->expr[j], eval->var_values, NULL);
     296             :         }
     297             :     }
     298             : 
     299        2592 :     samplesref->pts = eval->pts;
     300        2592 :     samplesref->sample_rate = eval->sample_rate;
     301        2592 :     eval->pts += eval->nb_samples;
     302             : 
     303        2592 :     return ff_filter_frame(outlink, samplesref);
     304             : }
     305             : 
     306             : #if CONFIG_AEVALSRC_FILTER
     307             : static const AVFilterPad aevalsrc_outputs[] = {
     308             :     {
     309             :         .name          = "default",
     310             :         .type          = AVMEDIA_TYPE_AUDIO,
     311             :         .config_props  = config_props,
     312             :         .request_frame = request_frame,
     313             :     },
     314             :     { NULL }
     315             : };
     316             : 
     317             : AVFilter ff_asrc_aevalsrc = {
     318             :     .name          = "aevalsrc",
     319             :     .description   = NULL_IF_CONFIG_SMALL("Generate an audio signal generated by an expression."),
     320             :     .query_formats = query_formats,
     321             :     .init          = init,
     322             :     .uninit        = uninit,
     323             :     .priv_size     = sizeof(EvalContext),
     324             :     .inputs        = NULL,
     325             :     .outputs       = aevalsrc_outputs,
     326             :     .priv_class    = &aevalsrc_class,
     327             : };
     328             : 
     329             : #endif /* CONFIG_AEVALSRC_FILTER */
     330             : 
     331             : #define OFFSET(x) offsetof(EvalContext, x)
     332             : #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
     333             : 
     334             : static const AVOption aeval_options[]= {
     335             :     { "exprs", "set the '|'-separated list of channels expressions", OFFSET(exprs), AV_OPT_TYPE_STRING, {.str = NULL}, .flags = FLAGS },
     336             :     { "channel_layout", "set channel layout", OFFSET(chlayout_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
     337             :     { "c",              "set channel layout", OFFSET(chlayout_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
     338             :     { NULL }
     339             : };
     340             : 
     341             : AVFILTER_DEFINE_CLASS(aeval);
     342             : 
     343           1 : static int aeval_query_formats(AVFilterContext *ctx)
     344             : {
     345           1 :     AVFilterFormats *formats = NULL;
     346             :     AVFilterChannelLayouts *layouts;
     347           1 :     AVFilterLink *inlink  = ctx->inputs[0];
     348           1 :     AVFilterLink *outlink  = ctx->outputs[0];
     349           1 :     EvalContext *eval = ctx->priv;
     350             :     static const enum AVSampleFormat sample_fmts[] = {
     351             :         AV_SAMPLE_FMT_DBLP, AV_SAMPLE_FMT_NONE
     352             :     };
     353             :     int ret;
     354             : 
     355             :     // inlink supports any channel layout
     356           1 :     layouts = ff_all_channel_counts();
     357           1 :     if ((ret = ff_channel_layouts_ref(layouts, &inlink->out_channel_layouts)) < 0)
     358           0 :         return ret;
     359             : 
     360           1 :     if (eval->same_chlayout) {
     361           0 :         layouts = ff_all_channel_counts();
     362           0 :         if ((ret = ff_set_common_channel_layouts(ctx, layouts)) < 0)
     363           0 :             return ret;
     364             :     } else {
     365             :         // outlink supports only requested output channel layout
     366           1 :         layouts = NULL;
     367           2 :         if ((ret = ff_add_channel_layout(&layouts,
     368           1 :                               eval->out_channel_layout ? eval->out_channel_layout :
     369           1 :                               FF_COUNT2LAYOUT(eval->nb_channels))) < 0)
     370           0 :             return ret;
     371           1 :         if ((ret = ff_channel_layouts_ref(layouts, &outlink->in_channel_layouts)) < 0)
     372           0 :             return ret;
     373             :     }
     374             : 
     375           1 :     formats = ff_make_format_list(sample_fmts);
     376           1 :     if ((ret = ff_set_common_formats(ctx, formats)) < 0)
     377           0 :         return ret;
     378             : 
     379           1 :     formats = ff_all_samplerates();
     380           1 :     return ff_set_common_samplerates(ctx, formats);
     381             : }
     382             : 
     383           1 : static int aeval_config_output(AVFilterLink *outlink)
     384             : {
     385           1 :     AVFilterContext *ctx = outlink->src;
     386           1 :     EvalContext *eval = ctx->priv;
     387           1 :     AVFilterLink *inlink = ctx->inputs[0];
     388             :     int ret;
     389             : 
     390           1 :     if (eval->same_chlayout) {
     391           0 :         eval->chlayout = inlink->channel_layout;
     392             : 
     393           0 :         if ((ret = parse_channel_expressions(ctx, inlink->channels)) < 0)
     394           0 :             return ret;
     395             :     }
     396             : 
     397           1 :     eval->n = 0;
     398           1 :     eval->nb_in_channels = eval->var_values[VAR_NB_IN_CHANNELS] = inlink->channels;
     399           1 :     eval->var_values[VAR_NB_OUT_CHANNELS] = outlink->channels;
     400           1 :     eval->var_values[VAR_S] = inlink->sample_rate;
     401           1 :     eval->var_values[VAR_T] = NAN;
     402             : 
     403           1 :     eval->channel_values = av_realloc_f(eval->channel_values,
     404           1 :                                         inlink->channels, sizeof(*eval->channel_values));
     405           1 :     if (!eval->channel_values)
     406           0 :         return AVERROR(ENOMEM);
     407             : 
     408           1 :     return 0;
     409             : }
     410             : 
     411             : #define TS2T(ts, tb) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts)*av_q2d(tb))
     412             : 
     413         130 : static int filter_frame(AVFilterLink *inlink, AVFrame *in)
     414             : {
     415         130 :     EvalContext *eval     = inlink->dst->priv;
     416         130 :     AVFilterLink *outlink = inlink->dst->outputs[0];
     417         130 :     int nb_samples        = in->nb_samples;
     418             :     AVFrame *out;
     419             :     double t0;
     420             :     int i, j;
     421             : 
     422             :     /* do volume scaling in-place if input buffer is writable */
     423         130 :     out = ff_get_audio_buffer(outlink, nb_samples);
     424         130 :     if (!out)
     425           0 :         return AVERROR(ENOMEM);
     426         130 :     av_frame_copy_props(out, in);
     427             : 
     428         130 :     t0 = TS2T(in->pts, inlink->time_base);
     429             : 
     430             :     /* evaluate expression for each single sample and for each channel */
     431      264730 :     for (i = 0; i < nb_samples; i++, eval->n++) {
     432      264600 :         eval->var_values[VAR_N] = eval->n;
     433      264600 :         eval->var_values[VAR_T] = t0 + i * (double)1/inlink->sample_rate;
     434             : 
     435      529200 :         for (j = 0; j < inlink->channels; j++)
     436      264600 :             eval->channel_values[j] = *((double *) in->extended_data[j] + i);
     437             : 
     438      529200 :         for (j = 0; j < outlink->channels; j++) {
     439      264600 :             eval->var_values[VAR_CH] = j;
     440      529200 :             *((double *) out->extended_data[j] + i) =
     441      264600 :                 av_expr_eval(eval->expr[j], eval->var_values, eval);
     442             :         }
     443             :     }
     444             : 
     445         130 :     av_frame_free(&in);
     446         130 :     return ff_filter_frame(outlink, out);
     447             : }
     448             : 
     449             : #if CONFIG_AEVAL_FILTER
     450             : 
     451             : static const AVFilterPad aeval_inputs[] = {
     452             :     {
     453             :         .name           = "default",
     454             :         .type           = AVMEDIA_TYPE_AUDIO,
     455             :         .filter_frame   = filter_frame,
     456             :     },
     457             :     { NULL }
     458             : };
     459             : 
     460             : static const AVFilterPad aeval_outputs[] = {
     461             :     {
     462             :         .name          = "default",
     463             :         .type          = AVMEDIA_TYPE_AUDIO,
     464             :         .config_props  = aeval_config_output,
     465             :     },
     466             :     { NULL }
     467             : };
     468             : 
     469             : AVFilter ff_af_aeval = {
     470             :     .name          = "aeval",
     471             :     .description   = NULL_IF_CONFIG_SMALL("Filter audio signal according to a specified expression."),
     472             :     .query_formats = aeval_query_formats,
     473             :     .init          = init,
     474             :     .uninit        = uninit,
     475             :     .priv_size     = sizeof(EvalContext),
     476             :     .inputs        = aeval_inputs,
     477             :     .outputs       = aeval_outputs,
     478             :     .priv_class    = &aeval_class,
     479             : };
     480             : 
     481             : #endif /* CONFIG_AEVAL_FILTER */

Generated by: LCOV version 1.12