LCOV - code coverage report
Current view: top level - src/libavfilter - af_compand.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 172 292 58.9 %
Date: 2017-01-23 11:54:22 Functions: 10 12 83.3 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 1999 Chris Bagwell
       3             :  * Copyright (c) 1999 Nick Bailey
       4             :  * Copyright (c) 2007 Rob Sykes <robs@users.sourceforge.net>
       5             :  * Copyright (c) 2013 Paul B Mahol
       6             :  * Copyright (c) 2014 Andrew Kelley
       7             :  *
       8             :  * This file is part of FFmpeg.
       9             :  *
      10             :  * FFmpeg is free software; you can redistribute it and/or
      11             :  * modify it under the terms of the GNU Lesser General Public
      12             :  * License as published by the Free Software Foundation; either
      13             :  * version 2.1 of the License, or (at your option) any later version.
      14             :  *
      15             :  * FFmpeg is distributed in the hope that it will be useful,
      16             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      18             :  * Lesser General Public License for more details.
      19             :  *
      20             :  * You should have received a copy of the GNU Lesser General Public
      21             :  * License along with FFmpeg; if not, write to the Free Software
      22             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
      23             :  */
      24             : 
      25             : /**
      26             :  * @file
      27             :  * audio compand filter
      28             :  */
      29             : 
      30             : #include "libavutil/avassert.h"
      31             : #include "libavutil/avstring.h"
      32             : #include "libavutil/ffmath.h"
      33             : #include "libavutil/opt.h"
      34             : #include "libavutil/samplefmt.h"
      35             : #include "audio.h"
      36             : #include "avfilter.h"
      37             : #include "internal.h"
      38             : 
      39             : typedef struct ChanParam {
      40             :     double attack;
      41             :     double decay;
      42             :     double volume;
      43             : } ChanParam;
      44             : 
      45             : typedef struct CompandSegment {
      46             :     double x, y;
      47             :     double a, b;
      48             : } CompandSegment;
      49             : 
      50             : typedef struct CompandContext {
      51             :     const AVClass *class;
      52             :     int nb_segments;
      53             :     char *attacks, *decays, *points;
      54             :     CompandSegment *segments;
      55             :     ChanParam *channels;
      56             :     double in_min_lin;
      57             :     double out_min_lin;
      58             :     double curve_dB;
      59             :     double gain_dB;
      60             :     double initial_volume;
      61             :     double delay;
      62             :     AVFrame *delay_frame;
      63             :     int delay_samples;
      64             :     int delay_count;
      65             :     int delay_index;
      66             :     int64_t pts;
      67             : 
      68             :     int (*compand)(AVFilterContext *ctx, AVFrame *frame);
      69             : } CompandContext;
      70             : 
      71             : #define OFFSET(x) offsetof(CompandContext, x)
      72             : #define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
      73             : 
      74             : static const AVOption compand_options[] = {
      75             :     { "attacks", "set time over which increase of volume is determined", OFFSET(attacks), AV_OPT_TYPE_STRING, { .str = "0.3" }, 0, 0, A },
      76             :     { "decays", "set time over which decrease of volume is determined", OFFSET(decays), AV_OPT_TYPE_STRING, { .str = "0.8" }, 0, 0, A },
      77             :     { "points", "set points of transfer function", OFFSET(points), AV_OPT_TYPE_STRING, { .str = "-70/-70|-60/-20" }, 0, 0, A },
      78             :     { "soft-knee", "set soft-knee", OFFSET(curve_dB), AV_OPT_TYPE_DOUBLE, { .dbl = 0.01 }, 0.01, 900, A },
      79             :     { "gain", "set output gain", OFFSET(gain_dB), AV_OPT_TYPE_DOUBLE, { .dbl = 0 }, -900, 900, A },
      80             :     { "volume", "set initial volume", OFFSET(initial_volume), AV_OPT_TYPE_DOUBLE, { .dbl = 0 }, -900, 0, A },
      81             :     { "delay", "set delay for samples before sending them to volume adjuster", OFFSET(delay), AV_OPT_TYPE_DOUBLE, { .dbl = 0 }, 0, 20, A },
      82             :     { NULL }
      83             : };
      84             : 
      85             : AVFILTER_DEFINE_CLASS(compand);
      86             : 
      87           2 : static av_cold int init(AVFilterContext *ctx)
      88             : {
      89           2 :     CompandContext *s = ctx->priv;
      90           2 :     s->pts            = AV_NOPTS_VALUE;
      91           2 :     return 0;
      92             : }
      93             : 
      94           3 : static av_cold void uninit(AVFilterContext *ctx)
      95             : {
      96           3 :     CompandContext *s = ctx->priv;
      97             : 
      98           3 :     av_freep(&s->channels);
      99           3 :     av_freep(&s->segments);
     100           3 :     av_frame_free(&s->delay_frame);
     101           3 : }
     102             : 
     103           1 : static int query_formats(AVFilterContext *ctx)
     104             : {
     105             :     AVFilterChannelLayouts *layouts;
     106             :     AVFilterFormats *formats;
     107             :     static const enum AVSampleFormat sample_fmts[] = {
     108             :         AV_SAMPLE_FMT_DBLP,
     109             :         AV_SAMPLE_FMT_NONE
     110             :     };
     111             :     int ret;
     112             : 
     113           1 :     layouts = ff_all_channel_counts();
     114           1 :     if (!layouts)
     115           0 :         return AVERROR(ENOMEM);
     116           1 :     ret = ff_set_common_channel_layouts(ctx, layouts);
     117           1 :     if (ret < 0)
     118           0 :         return ret;
     119             : 
     120           1 :     formats = ff_make_format_list(sample_fmts);
     121           1 :     if (!formats)
     122           0 :         return AVERROR(ENOMEM);
     123           1 :     ret = ff_set_common_formats(ctx, formats);
     124           1 :     if (ret < 0)
     125           0 :         return ret;
     126             : 
     127           1 :     formats = ff_all_samplerates();
     128           1 :     if (!formats)
     129           0 :         return AVERROR(ENOMEM);
     130           1 :     return ff_set_common_samplerates(ctx, formats);
     131             : }
     132             : 
     133           3 : static void count_items(char *item_str, int *nb_items)
     134             : {
     135             :     char *p;
     136             : 
     137           3 :     *nb_items = 1;
     138          36 :     for (p = item_str; *p; p++) {
     139          33 :         if (*p == ' ' || *p == '|')
     140           3 :             (*nb_items)++;
     141             :     }
     142           3 : }
     143             : 
     144       40960 : static void update_volume(ChanParam *cp, double in)
     145             : {
     146       40960 :     double delta = in - cp->volume;
     147             : 
     148       40960 :     if (delta > 0.0)
     149           0 :         cp->volume += delta * cp->attack;
     150             :     else
     151       40960 :         cp->volume += delta * cp->decay;
     152       40960 : }
     153             : 
     154       40960 : static double get_volume(CompandContext *s, double in_lin)
     155             : {
     156             :     CompandSegment *cs;
     157             :     double in_log, out_log;
     158             :     int i;
     159             : 
     160       40960 :     if (in_lin < s->in_min_lin)
     161           0 :         return s->out_min_lin;
     162             : 
     163       40960 :     in_log = log(in_lin);
     164             : 
     165      122966 :     for (i = 1; i < s->nb_segments; i++)
     166      122966 :         if (in_log <= s->segments[i].x)
     167       40960 :             break;
     168       40960 :     cs = &s->segments[i - 1];
     169       40960 :     in_log -= cs->x;
     170       40960 :     out_log = cs->y + in_log * (cs->a * in_log + cs->b);
     171             : 
     172       40960 :     return exp(out_log);
     173             : }
     174             : 
     175          20 : static int compand_nodelay(AVFilterContext *ctx, AVFrame *frame)
     176             : {
     177          20 :     CompandContext *s    = ctx->priv;
     178          20 :     AVFilterLink *inlink = ctx->inputs[0];
     179          20 :     const int channels   = inlink->channels;
     180          20 :     const int nb_samples = frame->nb_samples;
     181             :     AVFrame *out_frame;
     182             :     int chan, i;
     183             :     int err;
     184             : 
     185          20 :     if (av_frame_is_writable(frame)) {
     186          20 :         out_frame = frame;
     187             :     } else {
     188           0 :         out_frame = ff_get_audio_buffer(inlink, nb_samples);
     189           0 :         if (!out_frame) {
     190           0 :             av_frame_free(&frame);
     191           0 :             return AVERROR(ENOMEM);
     192             :         }
     193           0 :         err = av_frame_copy_props(out_frame, frame);
     194           0 :         if (err < 0) {
     195           0 :             av_frame_free(&out_frame);
     196           0 :             av_frame_free(&frame);
     197           0 :             return err;
     198             :         }
     199             :     }
     200             : 
     201          60 :     for (chan = 0; chan < channels; chan++) {
     202          40 :         const double *src = (double *)frame->extended_data[chan];
     203          40 :         double *dst = (double *)out_frame->extended_data[chan];
     204          40 :         ChanParam *cp = &s->channels[chan];
     205             : 
     206       41000 :         for (i = 0; i < nb_samples; i++) {
     207       40960 :             update_volume(cp, fabs(src[i]));
     208             : 
     209       40960 :             dst[i] = src[i] * get_volume(s, cp->volume);
     210             :         }
     211             :     }
     212             : 
     213          20 :     if (frame != out_frame)
     214           0 :         av_frame_free(&frame);
     215             : 
     216          20 :     return ff_filter_frame(ctx->outputs[0], out_frame);
     217             : }
     218             : 
     219             : #define MOD(a, b) (((a) >= (b)) ? (a) - (b) : (a))
     220             : 
     221           0 : static int compand_delay(AVFilterContext *ctx, AVFrame *frame)
     222             : {
     223           0 :     CompandContext *s    = ctx->priv;
     224           0 :     AVFilterLink *inlink = ctx->inputs[0];
     225           0 :     const int channels = inlink->channels;
     226           0 :     const int nb_samples = frame->nb_samples;
     227           0 :     int chan, i, av_uninit(dindex), oindex, av_uninit(count);
     228           0 :     AVFrame *out_frame   = NULL;
     229             :     int err;
     230             : 
     231           0 :     if (s->pts == AV_NOPTS_VALUE) {
     232           0 :         s->pts = (frame->pts == AV_NOPTS_VALUE) ? 0 : frame->pts;
     233             :     }
     234             : 
     235             :     av_assert1(channels > 0); /* would corrupt delay_count and delay_index */
     236             : 
     237           0 :     for (chan = 0; chan < channels; chan++) {
     238           0 :         AVFrame *delay_frame = s->delay_frame;
     239           0 :         const double *src    = (double *)frame->extended_data[chan];
     240           0 :         double *dbuf         = (double *)delay_frame->extended_data[chan];
     241           0 :         ChanParam *cp        = &s->channels[chan];
     242             :         double *dst;
     243             : 
     244           0 :         count  = s->delay_count;
     245           0 :         dindex = s->delay_index;
     246           0 :         for (i = 0, oindex = 0; i < nb_samples; i++) {
     247           0 :             const double in = src[i];
     248           0 :             update_volume(cp, fabs(in));
     249             : 
     250           0 :             if (count >= s->delay_samples) {
     251           0 :                 if (!out_frame) {
     252           0 :                     out_frame = ff_get_audio_buffer(inlink, nb_samples - i);
     253           0 :                     if (!out_frame) {
     254           0 :                         av_frame_free(&frame);
     255           0 :                         return AVERROR(ENOMEM);
     256             :                     }
     257           0 :                     err = av_frame_copy_props(out_frame, frame);
     258           0 :                     if (err < 0) {
     259           0 :                         av_frame_free(&out_frame);
     260           0 :                         av_frame_free(&frame);
     261           0 :                         return err;
     262             :                     }
     263           0 :                     out_frame->pts = s->pts;
     264           0 :                     s->pts += av_rescale_q(nb_samples - i,
     265           0 :                         (AVRational){ 1, inlink->sample_rate },
     266             :                         inlink->time_base);
     267             :                 }
     268             : 
     269           0 :                 dst = (double *)out_frame->extended_data[chan];
     270           0 :                 dst[oindex++] = dbuf[dindex] * get_volume(s, cp->volume);
     271             :             } else {
     272           0 :                 count++;
     273             :             }
     274             : 
     275           0 :             dbuf[dindex] = in;
     276           0 :             dindex = MOD(dindex + 1, s->delay_samples);
     277             :         }
     278             :     }
     279             : 
     280           0 :     s->delay_count = count;
     281           0 :     s->delay_index = dindex;
     282             : 
     283           0 :     av_frame_free(&frame);
     284             : 
     285           0 :     if (out_frame) {
     286           0 :         err = ff_filter_frame(ctx->outputs[0], out_frame);
     287           0 :         return err;
     288             :     }
     289             : 
     290           0 :     return 0;
     291             : }
     292             : 
     293           0 : static int compand_drain(AVFilterLink *outlink)
     294             : {
     295           0 :     AVFilterContext *ctx = outlink->src;
     296           0 :     CompandContext *s    = ctx->priv;
     297           0 :     const int channels   = outlink->channels;
     298           0 :     AVFrame *frame       = NULL;
     299             :     int chan, i, dindex;
     300             : 
     301             :     /* 2048 is to limit output frame size during drain */
     302           0 :     frame = ff_get_audio_buffer(outlink, FFMIN(2048, s->delay_count));
     303           0 :     if (!frame)
     304           0 :         return AVERROR(ENOMEM);
     305           0 :     frame->pts = s->pts;
     306           0 :     s->pts += av_rescale_q(frame->nb_samples,
     307           0 :             (AVRational){ 1, outlink->sample_rate }, outlink->time_base);
     308             : 
     309           0 :     av_assert0(channels > 0);
     310           0 :     for (chan = 0; chan < channels; chan++) {
     311           0 :         AVFrame *delay_frame = s->delay_frame;
     312           0 :         double *dbuf = (double *)delay_frame->extended_data[chan];
     313           0 :         double *dst = (double *)frame->extended_data[chan];
     314           0 :         ChanParam *cp = &s->channels[chan];
     315             : 
     316           0 :         dindex = s->delay_index;
     317           0 :         for (i = 0; i < frame->nb_samples; i++) {
     318           0 :             dst[i] = dbuf[dindex] * get_volume(s, cp->volume);
     319           0 :             dindex = MOD(dindex + 1, s->delay_samples);
     320             :         }
     321             :     }
     322           0 :     s->delay_count -= frame->nb_samples;
     323           0 :     s->delay_index = dindex;
     324             : 
     325           0 :     return ff_filter_frame(outlink, frame);
     326             : }
     327             : 
     328           1 : static int config_output(AVFilterLink *outlink)
     329             : {
     330           1 :     AVFilterContext *ctx  = outlink->src;
     331           1 :     CompandContext *s     = ctx->priv;
     332           1 :     const int sample_rate = outlink->sample_rate;
     333           1 :     double radius         = s->curve_dB * M_LN10 / 20.0;
     334           1 :     char *p, *saveptr     = NULL;
     335           1 :     const int channels    = outlink->channels;
     336             :     int nb_attacks, nb_decays, nb_points;
     337             :     int new_nb_items, num;
     338             :     int i;
     339             :     int err;
     340             : 
     341             : 
     342           1 :     count_items(s->attacks, &nb_attacks);
     343           1 :     count_items(s->decays, &nb_decays);
     344           1 :     count_items(s->points, &nb_points);
     345             : 
     346           1 :     if (channels <= 0) {
     347           0 :         av_log(ctx, AV_LOG_ERROR, "Invalid number of channels: %d\n", channels);
     348           0 :         return AVERROR(EINVAL);
     349             :     }
     350             : 
     351           1 :     if (nb_attacks > channels || nb_decays > channels) {
     352           0 :         av_log(ctx, AV_LOG_ERROR,
     353             :                 "Number of attacks/decays bigger than number of channels.\n");
     354           0 :         return AVERROR(EINVAL);
     355             :     }
     356             : 
     357           1 :     uninit(ctx);
     358             : 
     359           1 :     s->channels = av_mallocz_array(channels, sizeof(*s->channels));
     360           1 :     s->nb_segments = (nb_points + 4) * 2;
     361           1 :     s->segments = av_mallocz_array(s->nb_segments, sizeof(*s->segments));
     362             : 
     363           1 :     if (!s->channels || !s->segments) {
     364           0 :         uninit(ctx);
     365           0 :         return AVERROR(ENOMEM);
     366             :     }
     367             : 
     368           1 :     p = s->attacks;
     369           2 :     for (i = 0, new_nb_items = 0; i < nb_attacks; i++) {
     370           1 :         char *tstr = av_strtok(p, " |", &saveptr);
     371           1 :         p = NULL;
     372           1 :         new_nb_items += sscanf(tstr, "%lf", &s->channels[i].attack) == 1;
     373           1 :         if (s->channels[i].attack < 0) {
     374           0 :             uninit(ctx);
     375           0 :             return AVERROR(EINVAL);
     376             :         }
     377             :     }
     378           1 :     nb_attacks = new_nb_items;
     379             : 
     380           1 :     p = s->decays;
     381           2 :     for (i = 0, new_nb_items = 0; i < nb_decays; i++) {
     382           1 :         char *tstr = av_strtok(p, " |", &saveptr);
     383           1 :         p = NULL;
     384           1 :         new_nb_items += sscanf(tstr, "%lf", &s->channels[i].decay) == 1;
     385           1 :         if (s->channels[i].decay < 0) {
     386           0 :             uninit(ctx);
     387           0 :             return AVERROR(EINVAL);
     388             :         }
     389             :     }
     390           1 :     nb_decays = new_nb_items;
     391             : 
     392           1 :     if (nb_attacks != nb_decays) {
     393           0 :         av_log(ctx, AV_LOG_ERROR,
     394             :                 "Number of attacks %d differs from number of decays %d.\n",
     395             :                 nb_attacks, nb_decays);
     396           0 :         uninit(ctx);
     397           0 :         return AVERROR(EINVAL);
     398             :     }
     399             : 
     400           2 :     for (i = nb_decays; i < channels; i++) {
     401           1 :         s->channels[i].attack = s->channels[nb_decays - 1].attack;
     402           1 :         s->channels[i].decay = s->channels[nb_decays - 1].decay;
     403             :     }
     404             : 
     405             : #define S(x) s->segments[2 * ((x) + 1)]
     406           1 :     p = s->points;
     407           5 :     for (i = 0, new_nb_items = 0; i < nb_points; i++) {
     408           4 :         char *tstr = av_strtok(p, " |", &saveptr);
     409           4 :         p = NULL;
     410           4 :         if (sscanf(tstr, "%lf/%lf", &S(i).x, &S(i).y) != 2) {
     411           0 :             av_log(ctx, AV_LOG_ERROR,
     412             :                     "Invalid and/or missing input/output value.\n");
     413           0 :             uninit(ctx);
     414           0 :             return AVERROR(EINVAL);
     415             :         }
     416           4 :         if (i && S(i - 1).x > S(i).x) {
     417           0 :             av_log(ctx, AV_LOG_ERROR,
     418             :                     "Transfer function input values must be increasing.\n");
     419           0 :             uninit(ctx);
     420           0 :             return AVERROR(EINVAL);
     421             :         }
     422           4 :         S(i).y -= S(i).x;
     423           4 :         av_log(ctx, AV_LOG_DEBUG, "%d: x=%f y=%f\n", i, S(i).x, S(i).y);
     424           4 :         new_nb_items++;
     425             :     }
     426           1 :     num = new_nb_items;
     427             : 
     428             :     /* Add 0,0 if necessary */
     429           1 :     if (num == 0 || S(num - 1).x)
     430           1 :         num++;
     431             : 
     432             : #undef S
     433             : #define S(x) s->segments[2 * (x)]
     434             :     /* Add a tail off segment at the start */
     435           1 :     S(0).x = S(1).x - 2 * s->curve_dB;
     436           1 :     S(0).y = S(1).y;
     437           1 :     num++;
     438             : 
     439             :     /* Join adjacent colinear segments */
     440           5 :     for (i = 2; i < num; i++) {
     441           4 :         double g1 = (S(i - 1).y - S(i - 2).y) * (S(i - 0).x - S(i - 1).x);
     442           4 :         double g2 = (S(i - 0).y - S(i - 1).y) * (S(i - 1).x - S(i - 2).x);
     443             :         int j;
     444             : 
     445           4 :         if (fabs(g1 - g2))
     446           3 :             continue;
     447           1 :         num--;
     448           5 :         for (j = --i; j < num; j++)
     449           4 :             S(j) = S(j + 1);
     450             :     }
     451             : 
     452           9 :     for (i = 0; i < s->nb_segments; i += 2) {
     453           8 :         s->segments[i].y += s->gain_dB;
     454           8 :         s->segments[i].x *= M_LN10 / 20;
     455           8 :         s->segments[i].y *= M_LN10 / 20;
     456             :     }
     457             : 
     458             : #define L(x) s->segments[i - (x)]
     459           7 :     for (i = 4; i < s->nb_segments; i += 2) {
     460             :         double x, y, cx, cy, in1, in2, out1, out2, theta, len, r;
     461             : 
     462           6 :         L(4).a = 0;
     463           6 :         L(4).b = (L(2).y - L(4).y) / (L(2).x - L(4).x);
     464             : 
     465           6 :         L(2).a = 0;
     466           6 :         L(2).b = (L(0).y - L(2).y) / (L(0).x - L(2).x);
     467             : 
     468           6 :         theta = atan2(L(2).y - L(4).y, L(2).x - L(4).x);
     469           6 :         len = hypot(L(2).x - L(4).x, L(2).y - L(4).y);
     470           6 :         r = FFMIN(radius, len);
     471           6 :         L(3).x = L(2).x - r * cos(theta);
     472           6 :         L(3).y = L(2).y - r * sin(theta);
     473             : 
     474           6 :         theta = atan2(L(0).y - L(2).y, L(0).x - L(2).x);
     475           6 :         len = hypot(L(0).x - L(2).x, L(0).y - L(2).y);
     476           6 :         r = FFMIN(radius, len / 2);
     477           6 :         x = L(2).x + r * cos(theta);
     478           6 :         y = L(2).y + r * sin(theta);
     479             : 
     480           6 :         cx = (L(3).x + L(2).x + x) / 3;
     481           6 :         cy = (L(3).y + L(2).y + y) / 3;
     482             : 
     483           6 :         L(2).x = x;
     484           6 :         L(2).y = y;
     485             : 
     486           6 :         in1  = cx - L(3).x;
     487           6 :         out1 = cy - L(3).y;
     488           6 :         in2  = L(2).x - L(3).x;
     489           6 :         out2 = L(2).y - L(3).y;
     490           6 :         L(3).a = (out2 / in2 - out1 / in1) / (in2 - in1);
     491           6 :         L(3).b = out1 / in1 - L(3).a * in1;
     492             :     }
     493           1 :     L(3).x = 0;
     494           1 :     L(3).y = L(2).y;
     495             : 
     496           1 :     s->in_min_lin  = exp(s->segments[1].x);
     497           1 :     s->out_min_lin = exp(s->segments[1].y);
     498             : 
     499           3 :     for (i = 0; i < channels; i++) {
     500           2 :         ChanParam *cp = &s->channels[i];
     501             : 
     502           2 :         if (cp->attack > 1.0 / sample_rate)
     503           2 :             cp->attack = 1.0 - exp(-1.0 / (sample_rate * cp->attack));
     504             :         else
     505           0 :             cp->attack = 1.0;
     506           2 :         if (cp->decay > 1.0 / sample_rate)
     507           2 :             cp->decay = 1.0 - exp(-1.0 / (sample_rate * cp->decay));
     508             :         else
     509           0 :             cp->decay = 1.0;
     510           2 :         cp->volume = ff_exp10(s->initial_volume / 20);
     511             :     }
     512             : 
     513           1 :     s->delay_samples = s->delay * sample_rate;
     514           1 :     if (s->delay_samples <= 0) {
     515           1 :         s->compand = compand_nodelay;
     516           1 :         return 0;
     517             :     }
     518             : 
     519           0 :     s->delay_frame = av_frame_alloc();
     520           0 :     if (!s->delay_frame) {
     521           0 :         uninit(ctx);
     522           0 :         return AVERROR(ENOMEM);
     523             :     }
     524             : 
     525           0 :     s->delay_frame->format         = outlink->format;
     526           0 :     s->delay_frame->nb_samples     = s->delay_samples;
     527           0 :     s->delay_frame->channel_layout = outlink->channel_layout;
     528             : 
     529           0 :     err = av_frame_get_buffer(s->delay_frame, 32);
     530           0 :     if (err)
     531           0 :         return err;
     532             : 
     533           0 :     s->compand = compand_delay;
     534           0 :     return 0;
     535             : }
     536             : 
     537          20 : static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
     538             : {
     539          20 :     AVFilterContext *ctx = inlink->dst;
     540          20 :     CompandContext *s    = ctx->priv;
     541             : 
     542          20 :     return s->compand(ctx, frame);
     543             : }
     544             : 
     545          20 : static int request_frame(AVFilterLink *outlink)
     546             : {
     547          20 :     AVFilterContext *ctx = outlink->src;
     548          20 :     CompandContext *s    = ctx->priv;
     549          20 :     int ret = 0;
     550             : 
     551          20 :     ret = ff_request_frame(ctx->inputs[0]);
     552             : 
     553          20 :     if (ret == AVERROR_EOF && !ctx->is_disabled && s->delay_count)
     554           0 :         ret = compand_drain(outlink);
     555             : 
     556          20 :     return ret;
     557             : }
     558             : 
     559             : static const AVFilterPad compand_inputs[] = {
     560             :     {
     561             :         .name         = "default",
     562             :         .type         = AVMEDIA_TYPE_AUDIO,
     563             :         .filter_frame = filter_frame,
     564             :     },
     565             :     { NULL }
     566             : };
     567             : 
     568             : static const AVFilterPad compand_outputs[] = {
     569             :     {
     570             :         .name          = "default",
     571             :         .request_frame = request_frame,
     572             :         .config_props  = config_output,
     573             :         .type          = AVMEDIA_TYPE_AUDIO,
     574             :     },
     575             :     { NULL }
     576             : };
     577             : 
     578             : 
     579             : AVFilter ff_af_compand = {
     580             :     .name           = "compand",
     581             :     .description    = NULL_IF_CONFIG_SMALL(
     582             :             "Compress or expand audio dynamic range."),
     583             :     .query_formats  = query_formats,
     584             :     .priv_size      = sizeof(CompandContext),
     585             :     .priv_class     = &compand_class,
     586             :     .init           = init,
     587             :     .uninit         = uninit,
     588             :     .inputs         = compand_inputs,
     589             :     .outputs        = compand_outputs,
     590             : };

Generated by: LCOV version 1.12