Browse code

lavfi: add showspectrum filter.

Clément Bœsch authored on 2011/12/30 07:45:45
Showing 7 changed files
... ...
@@ -23,7 +23,7 @@ version next:
23 23
 - RTMPTS protocol support
24 24
 - RTMPE protocol support
25 25
 - RTMPTE protocol support
26
-- showwaves filter
26
+- showwaves and showspectrum filter
27 27
 - LucasArts SMUSH playback support
28 28
 - SAMI, RealText and SubViewer demuxers and decoders
29 29
 - Heart Of Darkness PAF playback support
... ...
@@ -1849,6 +1849,7 @@ pan_filter_deps="swresample"
1849 1849
 removelogo_filter_deps="avcodec avformat swscale"
1850 1850
 scale_filter_deps="swscale"
1851 1851
 select_filter_deps="avcodec"
1852
+showspectrum_filter_deps="avcodec"
1852 1853
 super2xsai_filter_deps="gpl"
1853 1854
 tinterlace_filter_deps="gpl"
1854 1855
 yadif_filter_deps="gpl"
... ...
@@ -4252,6 +4252,20 @@ do not have exactly the same duration in the first file.
4252 4252
 
4253 4253
 @end itemize
4254 4254
 
4255
+@section showspectrum
4256
+
4257
+Convert input audio to a video output, representing the audio frequency
4258
+spectrum.
4259
+
4260
+The filter accepts the following named parameters:
4261
+@table @option
4262
+@item size, s
4263
+Specify the video size for the output. Default value is @code{640x480}.
4264
+@end table
4265
+
4266
+The usage is very similar to the showwaves filter; see the examples in that
4267
+section.
4268
+
4255 4269
 @section showwaves
4256 4270
 
4257 4271
 Convert input audio to a video output, representing the samples waves.
... ...
@@ -202,6 +202,7 @@ OBJS-$(CONFIG_MP_FILTER) += libmpcodecs/pullup.o
202 202
 
203 203
 # multimedia filters
204 204
 OBJS-$(CONFIG_CONCAT_FILTER)                 += avf_concat.o
205
+OBJS-$(CONFIG_SHOWSPECTRUM_FILTER)           += avf_showspectrum.o
205 206
 OBJS-$(CONFIG_SHOWWAVES_FILTER)              += avf_showwaves.o
206 207
 
207 208
 # multimedia sources
... ...
@@ -143,6 +143,7 @@ void avfilter_register_all(void)
143 143
 
144 144
     /* multimedia filters */
145 145
     REGISTER_FILTER (CONCAT,      concat,      avf);
146
+    REGISTER_FILTER (SHOWSPECTRUM,showspectrum,avf);
146 147
     REGISTER_FILTER (SHOWWAVES,   showwaves,   avf);
147 148
 
148 149
     /* multimedia sources */
149 150
new file mode 100644
... ...
@@ -0,0 +1,314 @@
0
+/*
1
+ * Copyright (c) 2012 Clément Bœsch
2
+ *
3
+ * This file is part of FFmpeg.
4
+ *
5
+ * FFmpeg is free software; you can redistribute it and/or
6
+ * modify it under the terms of the GNU Lesser General Public
7
+ * License as published by the Free Software Foundation; either
8
+ * version 2.1 of the License, or (at your option) any later version.
9
+ *
10
+ * FFmpeg is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
+ * Lesser General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Lesser General Public
16
+ * License along with FFmpeg; if not, write to the Free Software
17
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
+ */
19
+
20
+/**
21
+ * @file
22
+ * audio to spectrum (video) transmedia filter, based on ffplay rdft showmode
23
+ * (by Michael Niedermayer) and lavfi/avf_showwaves (by Stefano Sabatini).
24
+ */
25
+
26
+#include <math.h>
27
+
28
+#include "libavcodec/avfft.h"
29
+#include "libavutil/audioconvert.h"
30
+#include "libavutil/opt.h"
31
+#include "avfilter.h"
32
+#include "internal.h"
33
+
34
+typedef struct {
35
+    const AVClass *class;
36
+    int w, h;
37
+    AVFilterBufferRef *outpicref;
38
+    int req_fullfilled;
39
+    int xpos;                   ///< x position (current column)
40
+    RDFTContext *rdft;          ///< Real Discrete Fourier Transform context
41
+    int rdft_bits;              ///< number of bits (RDFT window size = 1<<rdft_bits)
42
+    FFTSample *rdft_data;       ///< bins holder for each (displayed) channels
43
+    int filled;                 ///< number of samples (per channel) filled in current rdft_buffer
44
+    int consumed;               ///< number of samples (per channel) consumed from the input frame
45
+    float *window_func_lut;     ///< Window function LUT
46
+} ShowSpectrumContext;
47
+
48
+#define OFFSET(x) offsetof(ShowSpectrumContext, x)
49
+
50
+static const AVOption showspectrum_options[] = {
51
+    { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "640x480"}, 0, 0 },
52
+    { "s",    "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "640x480"}, 0, 0 },
53
+    { NULL },
54
+};
55
+
56
+AVFILTER_DEFINE_CLASS(showspectrum);
57
+
58
+static av_cold int init(AVFilterContext *ctx, const char *args)
59
+{
60
+    ShowSpectrumContext *showspectrum = ctx->priv;
61
+    int err;
62
+
63
+    showspectrum->class = &showspectrum_class;
64
+    av_opt_set_defaults(showspectrum);
65
+
66
+    if ((err = av_set_options_string(showspectrum, args, "=", ":")) < 0)
67
+        return err;
68
+
69
+    return 0;
70
+}
71
+
72
+static av_cold void uninit(AVFilterContext *ctx)
73
+{
74
+    ShowSpectrumContext *showspectrum = ctx->priv;
75
+
76
+    av_rdft_end(showspectrum->rdft);
77
+    av_freep(&showspectrum->rdft_data);
78
+    av_freep(&showspectrum->window_func_lut);
79
+    avfilter_unref_bufferp(&showspectrum->outpicref);
80
+}
81
+
82
+static int query_formats(AVFilterContext *ctx)
83
+{
84
+    AVFilterFormats *formats = NULL;
85
+    AVFilterChannelLayouts *layouts = NULL;
86
+    AVFilterLink *inlink = ctx->inputs[0];
87
+    AVFilterLink *outlink = ctx->outputs[0];
88
+    static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_S16P, -1 };
89
+    static const enum PixelFormat pix_fmts[] = { PIX_FMT_RGB24, -1 };
90
+
91
+    /* set input audio formats */
92
+    formats = ff_make_format_list(sample_fmts);
93
+    if (!formats)
94
+        return AVERROR(ENOMEM);
95
+    ff_formats_ref(formats, &inlink->out_formats);
96
+
97
+    layouts = ff_all_channel_layouts();
98
+    if (!layouts)
99
+        return AVERROR(ENOMEM);
100
+    ff_channel_layouts_ref(layouts, &inlink->out_channel_layouts);
101
+
102
+    formats = ff_all_samplerates();
103
+    if (!formats)
104
+        return AVERROR(ENOMEM);
105
+    ff_formats_ref(formats, &inlink->out_samplerates);
106
+
107
+    /* set output video format */
108
+    formats = ff_make_format_list(pix_fmts);
109
+    if (!formats)
110
+        return AVERROR(ENOMEM);
111
+    ff_formats_ref(formats, &outlink->in_formats);
112
+
113
+    return 0;
114
+}
115
+
116
+static int config_output(AVFilterLink *outlink)
117
+{
118
+    AVFilterContext *ctx = outlink->src;
119
+    ShowSpectrumContext *showspectrum = ctx->priv;
120
+    int i, rdft_bits, win_size;
121
+
122
+    outlink->w = showspectrum->w;
123
+    outlink->h = showspectrum->h;
124
+
125
+    /* RDFT window size (precision) according to the requested output frame height */
126
+    for (rdft_bits = 1; 1<<rdft_bits < 2*outlink->h; rdft_bits++);
127
+    win_size = 1 << rdft_bits;
128
+
129
+    /* (re-)configuration if the video output changed (or first init) */
130
+    if (rdft_bits != showspectrum->rdft_bits) {
131
+        AVFilterBufferRef *outpicref;
132
+
133
+        av_rdft_end(showspectrum->rdft);
134
+        showspectrum->rdft = av_rdft_init(rdft_bits, DFT_R2C);
135
+        showspectrum->rdft_bits = rdft_bits;
136
+
137
+        /* RDFT buffers: x2 for each (display) channel buffer */
138
+        showspectrum->rdft_data =
139
+            av_realloc_f(showspectrum->rdft_data, 2 * win_size,
140
+                         sizeof(*showspectrum->rdft_data));
141
+        if (!showspectrum->rdft_data)
142
+            return AVERROR(ENOMEM);
143
+        showspectrum->filled = 0;
144
+
145
+        /* pre-calc windowing function (hann here) */
146
+        showspectrum->window_func_lut =
147
+            av_realloc_f(showspectrum->window_func_lut, win_size,
148
+                         sizeof(*showspectrum->window_func_lut));
149
+        if (!showspectrum->window_func_lut)
150
+            return AVERROR(ENOMEM);
151
+        for (i = 0; i < win_size; i++)
152
+            showspectrum->window_func_lut[i] = .5f * (1 - cos(2*M_PI*i / (win_size-1)));
153
+
154
+        /* prepare the initial picref buffer (black frame) */
155
+        avfilter_unref_bufferp(&showspectrum->outpicref);
156
+        showspectrum->outpicref = outpicref =
157
+            ff_get_video_buffer(outlink, AV_PERM_WRITE|AV_PERM_PRESERVE|AV_PERM_REUSE2,
158
+                                outlink->w, outlink->h);
159
+        if (!outpicref)
160
+            return AVERROR(ENOMEM);
161
+        outlink->sample_aspect_ratio = (AVRational){1,1};
162
+        memset(outpicref->data[0], 0, outlink->h * outpicref->linesize[0]);
163
+    }
164
+
165
+    if (showspectrum->xpos >= outlink->w)
166
+        showspectrum->xpos = 0;
167
+
168
+    av_log(ctx, AV_LOG_VERBOSE, "s:%dx%d RDFT window size:%d\n",
169
+           showspectrum->w, showspectrum->h, win_size);
170
+    return 0;
171
+}
172
+
173
+inline static void push_frame(AVFilterLink *outlink)
174
+{
175
+    ShowSpectrumContext *showspectrum = outlink->src->priv;
176
+
177
+    showspectrum->xpos++;
178
+    if (showspectrum->xpos >= outlink->w)
179
+        showspectrum->xpos = 0;
180
+    showspectrum->filled = 0;
181
+    showspectrum->req_fullfilled = 1;
182
+
183
+    ff_start_frame(outlink, avfilter_ref_buffer(showspectrum->outpicref, ~AV_PERM_WRITE));
184
+    ff_draw_slice(outlink, 0, outlink->h, 1);
185
+    ff_end_frame(outlink);
186
+}
187
+
188
+static int request_frame(AVFilterLink *outlink)
189
+{
190
+    ShowSpectrumContext *showspectrum = outlink->src->priv;
191
+    AVFilterLink *inlink = outlink->src->inputs[0];
192
+    int ret;
193
+
194
+    showspectrum->req_fullfilled = 0;
195
+    do {
196
+        ret = ff_request_frame(inlink);
197
+    } while (!showspectrum->req_fullfilled && ret >= 0);
198
+
199
+    if (ret == AVERROR_EOF && showspectrum->outpicref)
200
+        push_frame(outlink);
201
+    return ret;
202
+}
203
+
204
+static int plot_spectrum_column(AVFilterLink *inlink, AVFilterBufferRef *insamples, int nb_samples)
205
+{
206
+    AVFilterContext *ctx = inlink->dst;
207
+    AVFilterLink *outlink = ctx->outputs[0];
208
+    ShowSpectrumContext *showspectrum = ctx->priv;
209
+    AVFilterBufferRef *outpicref = showspectrum->outpicref;
210
+    const int nb_channels = av_get_channel_layout_nb_channels(insamples->audio->channel_layout);
211
+
212
+    /* nb_freq contains the power of two superior or equal to the output image
213
+     * height (or half the RDFT window size) */
214
+    const int nb_freq = 1 << (showspectrum->rdft_bits - 1);
215
+    const int win_size = nb_freq << 1;
216
+
217
+    int ch, n, y;
218
+    FFTSample *data[2];
219
+    const int nb_display_channels = FFMIN(nb_channels, 2);
220
+    const int start = showspectrum->filled;
221
+    const int add_samples = FFMIN(win_size - start, nb_samples);
222
+
223
+    /* fill RDFT input with the number of samples available */
224
+    for (ch = 0; ch < nb_display_channels; ch++) {
225
+        const int16_t *p = (int16_t *)insamples->extended_data[ch];
226
+
227
+        p += showspectrum->consumed;
228
+        data[ch] = showspectrum->rdft_data + win_size * ch; // select channel buffer
229
+        for (n = 0; n < add_samples; n++)
230
+            data[ch][start + n] = p[n] * showspectrum->window_func_lut[start + n];
231
+    }
232
+    showspectrum->filled += add_samples;
233
+
234
+    /* complete RDFT window size? */
235
+    if (showspectrum->filled == win_size) {
236
+
237
+        /* run RDFT on each samples set */
238
+        for (ch = 0; ch < nb_display_channels; ch++)
239
+            av_rdft_calc(showspectrum->rdft, data[ch]);
240
+
241
+        /* fill a new spectrum column */
242
+#define RE(ch) data[ch][2*y + 0]
243
+#define IM(ch) data[ch][2*y + 1]
244
+#define MAGNITUDE(re, im) sqrt((re)*(re) + (im)*(im))
245
+
246
+        for (y = 0; y < outlink->h; y++) {
247
+            // FIXME: bin[0] contains first and last bins
248
+            const int pos = showspectrum->xpos * 3 + (outlink->h - y - 1) * outpicref->linesize[0];
249
+            const double w = 1. / sqrt(nb_freq);
250
+            int a =                           sqrt(w * MAGNITUDE(RE(0), IM(0)));
251
+            int b = nb_display_channels > 1 ? sqrt(w * MAGNITUDE(RE(1), IM(1))) : a;
252
+
253
+            a = FFMIN(a, 255);
254
+            b = FFMIN(b, 255);
255
+            outpicref->data[0][pos]   = a;
256
+            outpicref->data[0][pos+1] = b;
257
+            outpicref->data[0][pos+2] = (a + b) / 2;
258
+        }
259
+        outpicref->pts = insamples->pts +
260
+            av_rescale_q(showspectrum->consumed,
261
+                         (AVRational){ 1, inlink->sample_rate },
262
+                         outlink->time_base);
263
+        push_frame(outlink);
264
+    }
265
+
266
+    return add_samples;
267
+}
268
+
269
+static int filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
270
+{
271
+    AVFilterContext *ctx = inlink->dst;
272
+    ShowSpectrumContext *showspectrum = ctx->priv;
273
+    int left_samples = insamples->audio->nb_samples;
274
+
275
+    showspectrum->consumed = 0;
276
+    while (left_samples) {
277
+        const int added_samples = plot_spectrum_column(inlink, insamples, left_samples);
278
+        showspectrum->consumed += added_samples;
279
+        left_samples -= added_samples;
280
+    }
281
+
282
+    avfilter_unref_buffer(insamples);
283
+    return 0;
284
+}
285
+
286
+AVFilter avfilter_avf_showspectrum = {
287
+    .name           = "showspectrum",
288
+    .description    = NULL_IF_CONFIG_SMALL("Convert input audio to a spectrum video output."),
289
+    .init           = init,
290
+    .uninit         = uninit,
291
+    .query_formats  = query_formats,
292
+    .priv_size      = sizeof(ShowSpectrumContext),
293
+
294
+    .inputs  = (const AVFilterPad[]) {
295
+        {
296
+            .name           = "default",
297
+            .type           = AVMEDIA_TYPE_AUDIO,
298
+            .filter_samples = filter_samples,
299
+            .min_perms      = AV_PERM_READ,
300
+        },
301
+        { .name = NULL }
302
+    },
303
+
304
+    .outputs = (const AVFilterPad[]) {
305
+        {
306
+            .name           = "default",
307
+            .type           = AVMEDIA_TYPE_VIDEO,
308
+            .config_props   = config_output,
309
+            .request_frame  = request_frame,
310
+        },
311
+        { .name = NULL }
312
+    },
313
+};
... ...
@@ -29,8 +29,8 @@
29 29
 #include "libavutil/avutil.h"
30 30
 
31 31
 #define LIBAVFILTER_VERSION_MAJOR  3
32
-#define LIBAVFILTER_VERSION_MINOR  11
33
-#define LIBAVFILTER_VERSION_MICRO 101
32
+#define LIBAVFILTER_VERSION_MINOR  12
33
+#define LIBAVFILTER_VERSION_MICRO 100
34 34
 
35 35
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
36 36
                                                LIBAVFILTER_VERSION_MINOR, \