Browse code

avfilter: add aphasemeter filter

Paul B Mahol authored on 2015/08/11 23:37:45
Showing 7 changed files
... ...
@@ -30,6 +30,7 @@ version <next>:
30 30
 - allyuv video source
31 31
 - atadenoise video filter
32 32
 - OS X VideoToolbox support
33
+- aphasemeter filter
33 34
 
34 35
 
35 36
 version 2.7:
... ...
@@ -354,6 +354,7 @@ Filters:
354 354
   af_pan.c                              Nicolas George
355 355
   af_sidechaincompress.c                Paul B Mahol
356 356
   af_silenceremove.c                    Paul B Mahol
357
+  avf_aphasemeter.c                     Paul B Mahol
357 358
   avf_avectorscope.c                    Paul B Mahol
358 359
   avf_showcqt.c                         Muhammad Faiz
359 360
   vf_blend.c                            Paul B Mahol
... ...
@@ -11656,6 +11656,38 @@ tools.
11656 11656
 
11657 11657
 Below is a description of the currently available multimedia filters.
11658 11658
 
11659
+@section aphasemeter
11660
+
11661
+Convert input audio to a video output, displaying the audio phase.
11662
+
11663
+The filter accepts the following options:
11664
+
11665
+@table @option
11666
+@item rate, r
11667
+Set the output frame rate. Default value is @code{25}.
11668
+
11669
+@item size, s
11670
+Set the video size for the output. For the syntax of this option, check the
11671
+@ref{video size syntax,,"Video size" section in the ffmpeg-utils manual,ffmpeg-utils}.
11672
+Default value is @code{800x400}.
11673
+
11674
+@item rc
11675
+@item gc
11676
+@item bc
11677
+Specify the red, green, blue contrast. Default values are @code{2},
11678
+@code{7} and @code{1}.
11679
+Allowed range is @code{[0, 255]}.
11680
+
11681
+@item mpc
11682
+Set color which will be used for drawing median phase. If color is
11683
+@code{none} which is default, no median phase value will be drawn.
11684
+@end table
11685
+
11686
+The filter also exports the frame metadata @code{lavfi.aphasemeter.phase} which
11687
+represents mean phase of current audio frame. Value is in range @code{[-1, 1]}.
11688
+The @code{-1} means left and right channels are completely out of phase and
11689
+@code{1} means channels are in phase.
11690
+
11659 11691
 @section avectorscope
11660 11692
 
11661 11693
 Convert input audio to a video output, representing the audio vector
... ...
@@ -253,6 +253,7 @@ OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
253 253
 
254 254
 # multimedia filters
255 255
 OBJS-$(CONFIG_ADRAWGRAPH_FILTER)             += f_drawgraph.o
256
+OBJS-$(CONFIG_APHASEMETER_FILTER)            += avf_aphasemeter.o
256 257
 OBJS-$(CONFIG_AVECTORSCOPE_FILTER)           += avf_avectorscope.o
257 258
 OBJS-$(CONFIG_CONCAT_FILTER)                 += avf_concat.o
258 259
 OBJS-$(CONFIG_SHOWCQT_FILTER)                += avf_showcqt.o
... ...
@@ -268,6 +268,7 @@ void avfilter_register_all(void)
268 268
 
269 269
     /* multimedia filters */
270 270
     REGISTER_FILTER(ADRAWGRAPH,     adrawgraph,     avf);
271
+    REGISTER_FILTER(APHASEMETER,    aphasemeter,    avf);
271 272
     REGISTER_FILTER(AVECTORSCOPE,   avectorscope,   avf);
272 273
     REGISTER_FILTER(CONCAT,         concat,         avf);
273 274
     REGISTER_FILTER(SHOWCQT,        showcqt,        avf);
274 275
new file mode 100644
... ...
@@ -0,0 +1,241 @@
0
+/*
1
+ * Copyright (c) 2015 Paul B Mahol
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 video multimedia aphasemeter filter
23
+ */
24
+
25
+#include "libavutil/avassert.h"
26
+#include "libavutil/channel_layout.h"
27
+#include "libavutil/intreadwrite.h"
28
+#include "libavutil/opt.h"
29
+#include "libavutil/parseutils.h"
30
+#include "avfilter.h"
31
+#include "formats.h"
32
+#include "audio.h"
33
+#include "video.h"
34
+#include "internal.h"
35
+
36
+typedef struct AudioPhaseMeterContext {
37
+    const AVClass *class;
38
+    AVFrame *out;
39
+    int w, h;
40
+    AVRational frame_rate;
41
+    int contrast[4];
42
+    uint8_t *mpc_str;
43
+    uint8_t mpc[4];
44
+    int draw_median_phase;
45
+} AudioPhaseMeterContext;
46
+
47
+#define OFFSET(x) offsetof(AudioPhaseMeterContext, x)
48
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
49
+
50
+static const AVOption aphasemeter_options[] = {
51
+    { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, 0, FLAGS },
52
+    { "r",    "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, 0, FLAGS },
53
+    { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="800x400"}, 0, 0, FLAGS },
54
+    { "s",    "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="800x400"}, 0, 0, FLAGS },
55
+    { "rc", "set red contrast",   OFFSET(contrast[0]), AV_OPT_TYPE_INT, {.i64=2}, 0, 255, FLAGS },
56
+    { "gc", "set green contrast", OFFSET(contrast[1]), AV_OPT_TYPE_INT, {.i64=7}, 0, 255, FLAGS },
57
+    { "bc", "set blue contrast",  OFFSET(contrast[2]), AV_OPT_TYPE_INT, {.i64=1}, 0, 255, FLAGS },
58
+    { "mpc", "set median phase color", OFFSET(mpc_str), AV_OPT_TYPE_STRING, {.str = "none"}, 0, 0, FLAGS },
59
+    { NULL }
60
+};
61
+
62
+AVFILTER_DEFINE_CLASS(aphasemeter);
63
+
64
+static int query_formats(AVFilterContext *ctx)
65
+{
66
+    AVFilterFormats *formats = NULL;
67
+    AVFilterChannelLayouts *layout = NULL;
68
+    AVFilterLink *inlink = ctx->inputs[0];
69
+    AVFilterLink *outlink = ctx->outputs[0];
70
+    static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_NONE };
71
+    static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_RGBA, AV_PIX_FMT_NONE };
72
+
73
+    formats = ff_make_format_list(sample_fmts);
74
+    if (!formats)
75
+        return AVERROR(ENOMEM);
76
+    ff_formats_ref(formats, &inlink->out_formats);
77
+
78
+    ff_add_channel_layout(&layout, AV_CH_LAYOUT_STEREO);
79
+    ff_channel_layouts_ref(layout, &inlink->out_channel_layouts);
80
+
81
+    formats = ff_all_samplerates();
82
+    if (!formats)
83
+        return AVERROR(ENOMEM);
84
+    ff_formats_ref(formats, &inlink->out_samplerates);
85
+
86
+    formats = ff_make_format_list(pix_fmts);
87
+    if (!formats)
88
+        return AVERROR(ENOMEM);
89
+    ff_formats_ref(formats, &outlink->in_formats);
90
+
91
+    return 0;
92
+}
93
+
94
+static int config_input(AVFilterLink *inlink)
95
+{
96
+    AVFilterContext *ctx = inlink->dst;
97
+    AudioPhaseMeterContext *s = ctx->priv;
98
+    int nb_samples;
99
+
100
+    nb_samples = FFMAX(1024, ((double)inlink->sample_rate / av_q2d(s->frame_rate)) + 0.5);
101
+    inlink->partial_buf_size =
102
+    inlink->min_samples =
103
+    inlink->max_samples = nb_samples;
104
+
105
+    return 0;
106
+}
107
+
108
+static int config_output(AVFilterLink *outlink)
109
+{
110
+    AVFilterContext *ctx = outlink->src;
111
+    AudioPhaseMeterContext *s = ctx->priv;
112
+
113
+    outlink->w = s->w;
114
+    outlink->h = s->h;
115
+    outlink->sample_aspect_ratio = (AVRational){1,1};
116
+    outlink->frame_rate = s->frame_rate;
117
+
118
+    if (!strcmp(s->mpc_str, "none"))
119
+        s->draw_median_phase = 0;
120
+    else if (av_parse_color(s->mpc, s->mpc_str, -1, ctx) >= 0)
121
+        s->draw_median_phase = 1;
122
+    else
123
+        return AVERROR(EINVAL);
124
+
125
+    return 0;
126
+}
127
+
128
+static inline int get_x(float phase, int w)
129
+{
130
+  return (phase + 1.) / 2. * (w - 1);
131
+}
132
+
133
+static int filter_frame(AVFilterLink *inlink, AVFrame *in)
134
+{
135
+    AVFilterContext *ctx = inlink->dst;
136
+    AVFilterLink *outlink = ctx->outputs[0];
137
+    AudioPhaseMeterContext *s = ctx->priv;
138
+    AVDictionary **metadata;
139
+    const int rc = s->contrast[0];
140
+    const int gc = s->contrast[1];
141
+    const int bc = s->contrast[2];
142
+    float fphase = 0;
143
+    AVFrame *out;
144
+    uint8_t *dst;
145
+    int i;
146
+
147
+    if (!s->out || s->out->width  != outlink->w ||
148
+                   s->out->height != outlink->h) {
149
+        av_frame_free(&s->out);
150
+        s->out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
151
+        if (!s->out) {
152
+            av_frame_free(&in);
153
+            return AVERROR(ENOMEM);
154
+        }
155
+
156
+        out = s->out;
157
+        for (i = 0; i < outlink->h; i++)
158
+            memset(out->data[0] + i * out->linesize[0], 0, outlink->w * 4);
159
+    } else {
160
+        out = s->out;
161
+        for (i = outlink->h - 1; i >= 10; i--)
162
+            memmove(out->data[0] + (i  ) * out->linesize[0],
163
+                    out->data[0] + (i-1) * out->linesize[0],
164
+                    outlink->w * 4);
165
+        for (i = 0; i < outlink->w; i++)
166
+            AV_WL32(out->data[0] + i * 4, 0);
167
+    }
168
+    s->out->pts = in->pts;
169
+
170
+    for (i = 0; i < in->nb_samples; i++) {
171
+        const float *src = (float *)in->data[0] + i * 2;
172
+        const float f = src[0] * src[1] / (src[0]*src[0] + src[1] * src[1]) * 2;
173
+        const float phase = isnan(f) ? 1 : f;
174
+        const int x = get_x(phase, s->w);
175
+
176
+        dst = out->data[0] + x * 4;
177
+        dst[0] = FFMIN(255, dst[0] + rc);
178
+        dst[1] = FFMIN(255, dst[1] + gc);
179
+        dst[2] = FFMIN(255, dst[2] + bc);
180
+        dst[3] = 255;
181
+        fphase += phase;
182
+    }
183
+    fphase /= in->nb_samples;
184
+
185
+    if (s->draw_median_phase) {
186
+        dst = out->data[0] + get_x(fphase, s->w) * 4;
187
+        AV_WL32(dst, AV_RL32(s->mpc));
188
+    }
189
+
190
+    for (i = 1; i < 10 && i < outlink->h; i++)
191
+        memcpy(out->data[0] + i * out->linesize[0], out->data[0], outlink->w * 4);
192
+
193
+    metadata = avpriv_frame_get_metadatap(out);
194
+    if (metadata) {
195
+        uint8_t value[128];
196
+
197
+        snprintf(value, sizeof(value), "%f", fphase);
198
+        av_dict_set(metadata, "lavfi.aphasemeter.phase", value, 0);
199
+    }
200
+
201
+    av_frame_free(&in);
202
+    return ff_filter_frame(outlink, av_frame_clone(s->out));
203
+}
204
+
205
+static av_cold void uninit(AVFilterContext *ctx)
206
+{
207
+    AudioPhaseMeterContext *s = ctx->priv;
208
+
209
+    av_frame_free(&s->out);
210
+}
211
+
212
+static const AVFilterPad inputs[] = {
213
+    {
214
+        .name         = "default",
215
+        .type         = AVMEDIA_TYPE_AUDIO,
216
+        .config_props = config_input,
217
+        .filter_frame = filter_frame,
218
+    },
219
+    { NULL }
220
+};
221
+
222
+static const AVFilterPad outputs[] = {
223
+    {
224
+        .name         = "default",
225
+        .type         = AVMEDIA_TYPE_VIDEO,
226
+        .config_props = config_output,
227
+    },
228
+    { NULL }
229
+};
230
+
231
+AVFilter ff_avf_aphasemeter = {
232
+    .name          = "aphasemeter",
233
+    .description   = NULL_IF_CONFIG_SMALL("Convert input audio to phase meter video output."),
234
+    .uninit        = uninit,
235
+    .query_formats = query_formats,
236
+    .priv_size     = sizeof(AudioPhaseMeterContext),
237
+    .inputs        = inputs,
238
+    .outputs       = outputs,
239
+    .priv_class    = &aphasemeter_class,
240
+};
... ...
@@ -30,7 +30,7 @@
30 30
 #include "libavutil/version.h"
31 31
 
32 32
 #define LIBAVFILTER_VERSION_MAJOR  5
33
-#define LIBAVFILTER_VERSION_MINOR  32
33
+#define LIBAVFILTER_VERSION_MINOR  33
34 34
 #define LIBAVFILTER_VERSION_MICRO 100
35 35
 
36 36
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \