avfilter: add amultiply audio filter
[ffmpeg.git] / libavfilter / af_amultiply.c
1 /*
2  * Copyright (c) 2018 Paul B Mahol
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 #include "libavutil/avassert.h"
22 #include "libavutil/channel_layout.h"
23 #include "libavutil/common.h"
24 #include "libavutil/float_dsp.h"
25 #include "libavutil/opt.h"
26
27 #define FF_INTERNAL_FIELDS 1
28 #include "framequeue.h"
29
30 #include "audio.h"
31 #include "avfilter.h"
32 #include "formats.h"
33 #include "filters.h"
34 #include "internal.h"
35
36 typedef struct AudioMultiplyContext {
37     const AVClass *class;
38
39     AVFrame *frames[2];
40     int64_t pts;
41     int planes;
42     int channels;
43     int samples_align;
44
45     AVFloatDSPContext *fdsp;
46 } AudioMultiplyContext;
47
48 static int query_formats(AVFilterContext *ctx)
49 {
50     AVFilterFormats *formats;
51     AVFilterChannelLayouts *layouts;
52     static const enum AVSampleFormat sample_fmts[] = {
53         AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP,
54         AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBLP,
55         AV_SAMPLE_FMT_NONE
56     };
57     int ret;
58
59     layouts = ff_all_channel_counts();
60     if (!layouts)
61         return AVERROR(ENOMEM);
62     ret = ff_set_common_channel_layouts(ctx, layouts);
63     if (ret < 0)
64         return ret;
65
66     formats = ff_make_format_list(sample_fmts);
67     if (!formats)
68         return AVERROR(ENOMEM);
69     ret = ff_set_common_formats(ctx, formats);
70     if (ret < 0)
71         return ret;
72
73     formats = ff_all_samplerates();
74     if (!formats)
75         return AVERROR(ENOMEM);
76     return ff_set_common_samplerates(ctx, formats);
77 }
78
79 static int activate(AVFilterContext *ctx)
80 {
81     AudioMultiplyContext *s = ctx->priv;
82     int i, ret, status;
83     int nb_samples;
84     int64_t pts;
85
86     FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[0], ctx);
87
88     nb_samples = FFMIN(ff_framequeue_queued_samples(&ctx->inputs[0]->fifo),
89                        ff_framequeue_queued_samples(&ctx->inputs[1]->fifo));
90     for (i = 0; i < ctx->nb_inputs && nb_samples > 0; i++) {
91         if (s->frames[i])
92             continue;
93
94         if (ff_inlink_check_available_samples(ctx->inputs[i], nb_samples) > 0) {
95             ret = ff_inlink_consume_samples(ctx->inputs[i], nb_samples, nb_samples, &s->frames[i]);
96             if (ret < 0)
97                 return ret;
98         }
99     }
100
101     if (nb_samples > 0 && s->frames[0] && s->frames[1]) {
102         AVFrame *out;
103         int plane_samples;
104
105         if (av_sample_fmt_is_planar(ctx->inputs[0]->format))
106             plane_samples = FFALIGN(nb_samples, s->samples_align);
107         else
108             plane_samples = FFALIGN(nb_samples * s->channels, s->samples_align);
109
110         out = ff_get_audio_buffer(ctx->outputs[0], nb_samples);
111         if (!out)
112             return AVERROR(ENOMEM);
113
114         out->pts = s->pts;
115         s->pts += nb_samples;
116
117         if (av_get_packed_sample_fmt(ctx->inputs[0]->format) == AV_SAMPLE_FMT_FLT) {
118             for (i = 0; i < s->planes; i++) {
119                 s->fdsp->vector_fmul((float *)out->extended_data[i],
120                                      (const float *)s->frames[0]->extended_data[i],
121                                      (const float *)s->frames[1]->extended_data[i],
122                                      plane_samples);
123             }
124         } else {
125             for (i = 0; i < s->planes; i++) {
126                 s->fdsp->vector_dmul((double *)out->extended_data[i],
127                                      (const double *)s->frames[0]->extended_data[i],
128                                      (const double *)s->frames[1]->extended_data[i],
129                                      plane_samples);
130             }
131         }
132         emms_c();
133
134         av_frame_free(&s->frames[0]);
135         av_frame_free(&s->frames[1]);
136
137         ret = ff_filter_frame(ctx->outputs[0], out);
138         if (ret < 0)
139             return ret;
140     }
141
142     if (!nb_samples) {
143         for (i = 0; i < 2; i++) {
144             if (ff_inlink_acknowledge_status(ctx->inputs[i], &status, &pts)) {
145                 ff_outlink_set_status(ctx->outputs[0], status, pts);
146                 return 0;
147             }
148         }
149     }
150
151     if (ff_outlink_frame_wanted(ctx->outputs[0])) {
152         for (i = 0; i < 2; i++) {
153             if (ff_framequeue_queued_samples(&ctx->inputs[i]->fifo) > 0)
154                 continue;
155             ff_inlink_request_frame(ctx->inputs[i]);
156             return 0;
157         }
158     }
159     return 0;
160 }
161
162 static int config_output(AVFilterLink *outlink)
163 {
164     AVFilterContext *ctx = outlink->src;
165     AudioMultiplyContext *s = ctx->priv;
166     AVFilterLink *inlink = ctx->inputs[0];
167
168     s->channels = inlink->channels;
169     s->planes = av_sample_fmt_is_planar(inlink->format) ? inlink->channels : 1;
170     s->samples_align = 16;
171
172     return 0;
173 }
174
175 static av_cold int init(AVFilterContext *ctx)
176 {
177     AudioMultiplyContext *s = ctx->priv;
178
179     s->fdsp = avpriv_float_dsp_alloc(0);
180     if (!s->fdsp)
181         return AVERROR(ENOMEM);
182
183     return 0;
184 }
185
186 static av_cold void uninit(AVFilterContext *ctx)
187 {
188     AudioMultiplyContext *s = ctx->priv;
189     av_freep(&s->fdsp);
190 }
191
192 static const AVFilterPad inputs[] = {
193     {
194         .name = "multiply0",
195         .type = AVMEDIA_TYPE_AUDIO,
196     },
197     {
198         .name = "multiply1",
199         .type = AVMEDIA_TYPE_AUDIO,
200     },
201     { NULL }
202 };
203
204 static const AVFilterPad outputs[] = {
205     {
206         .name         = "default",
207         .type         = AVMEDIA_TYPE_AUDIO,
208         .config_props = config_output,
209     },
210     { NULL }
211 };
212
213 AVFilter ff_af_amultiply = {
214     .name           = "amultiply",
215     .description    = NULL_IF_CONFIG_SMALL("Multiply two audio streams."),
216     .priv_size      = sizeof(AudioMultiplyContext),
217     .init           = init,
218     .uninit         = uninit,
219     .activate       = activate,
220     .query_formats  = query_formats,
221     .inputs         = inputs,
222     .outputs        = outputs,
223 };