2 * Copyright (c) 2013 Paul B Mahol
4 * This file is part of FFmpeg.
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.
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.
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
23 * audio to video multimedia vectorscope filter
26 #include "libavutil/avassert.h"
27 #include "libavutil/channel_layout.h"
28 #include "libavutil/opt.h"
29 #include "libavutil/parseutils.h"
36 enum VectorScopeMode
{
42 typedef struct AudioVectorScopeContext
{
47 enum VectorScopeMode mode
;
51 AVRational frame_rate
;
52 } AudioVectorScopeContext
;
54 #define OFFSET(x) offsetof(AudioVectorScopeContext, x)
55 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
57 static const AVOption avectorscope_options
[] = {
58 { "mode", "set mode", OFFSET(mode
), AV_OPT_TYPE_INT
, {.i64
=LISSAJOUS
}, 0, MODE_NB
-1, FLAGS
, "mode" },
59 { "m", "set mode", OFFSET(mode
), AV_OPT_TYPE_INT
, {.i64
=LISSAJOUS
}, 0, MODE_NB
-1, FLAGS
, "mode" },
60 { "lissajous", "", 0, AV_OPT_TYPE_CONST
, {.i64
=LISSAJOUS
}, 0, 0, FLAGS
, "mode" },
61 { "lissajous_xy", "", 0, AV_OPT_TYPE_CONST
, {.i64
=LISSAJOUS_XY
}, 0, 0, FLAGS
, "mode" },
62 { "rate", "set video rate", OFFSET(frame_rate
), AV_OPT_TYPE_VIDEO_RATE
, {.str
="25"}, 0, 0, FLAGS
},
63 { "r", "set video rate", OFFSET(frame_rate
), AV_OPT_TYPE_VIDEO_RATE
, {.str
="25"}, 0, 0, FLAGS
},
64 { "size", "set video size", OFFSET(w
), AV_OPT_TYPE_IMAGE_SIZE
, {.str
="400x400"}, 0, 0, FLAGS
},
65 { "s", "set video size", OFFSET(w
), AV_OPT_TYPE_IMAGE_SIZE
, {.str
="400x400"}, 0, 0, FLAGS
},
66 { "rc", "set red contrast", OFFSET(contrast
[0]), AV_OPT_TYPE_INT
, {.i64
=40}, 0, 255, FLAGS
},
67 { "gc", "set green contrast", OFFSET(contrast
[1]), AV_OPT_TYPE_INT
, {.i64
=160}, 0, 255, FLAGS
},
68 { "bc", "set blue contrast", OFFSET(contrast
[2]), AV_OPT_TYPE_INT
, {.i64
=80}, 0, 255, FLAGS
},
69 { "rf", "set red fade", OFFSET(fade
[0]), AV_OPT_TYPE_INT
, {.i64
=15}, 0, 255, FLAGS
},
70 { "gf", "set green fade", OFFSET(fade
[1]), AV_OPT_TYPE_INT
, {.i64
=10}, 0, 255, FLAGS
},
71 { "bf", "set blue fade", OFFSET(fade
[2]), AV_OPT_TYPE_INT
, {.i64
=5}, 0, 255, FLAGS
},
72 { "zoom", "set zoom factor", OFFSET(zoom
), AV_OPT_TYPE_DOUBLE
, {.dbl
=1}, 1, 10, FLAGS
},
76 AVFILTER_DEFINE_CLASS(avectorscope
);
78 static void draw_dot(AudioVectorScopeContext
*p
, unsigned x
, unsigned y
)
80 const int linesize
= p
->outpicref
->linesize
[0];
84 if (y
>= p
->h
|| x
>= p
->w
)
87 y
= FFMIN(y
, p
->h
- 1);
88 x
= FFMIN(x
, p
->w
- 1);
91 dst
= &p
->outpicref
->data
[0][y
* linesize
+ x
* 4];
92 dst
[0] = FFMIN(dst
[0] + p
->contrast
[0], 255);
93 dst
[1] = FFMIN(dst
[1] + p
->contrast
[1], 255);
94 dst
[2] = FFMIN(dst
[2] + p
->contrast
[2], 255);
97 static void fade(AudioVectorScopeContext
*p
)
99 const int linesize
= p
->outpicref
->linesize
[0];
102 if (p
->fade
[0] || p
->fade
[1] || p
->fade
[2]) {
103 uint8_t *d
= p
->outpicref
->data
[0];
104 for (i
= 0; i
< p
->h
; i
++) {
105 for (j
= 0; j
< p
->w
*4; j
+=4) {
106 d
[j
+0] = FFMAX(d
[j
+0] - p
->fade
[0], 0);
107 d
[j
+1] = FFMAX(d
[j
+1] - p
->fade
[1], 0);
108 d
[j
+2] = FFMAX(d
[j
+2] - p
->fade
[2], 0);
115 static int query_formats(AVFilterContext
*ctx
)
117 AVFilterFormats
*formats
= NULL
;
118 AVFilterChannelLayouts
*layout
= NULL
;
119 AVFilterLink
*inlink
= ctx
->inputs
[0];
120 AVFilterLink
*outlink
= ctx
->outputs
[0];
121 static const enum AVSampleFormat sample_fmts
[] = { AV_SAMPLE_FMT_S16
, AV_SAMPLE_FMT_FLT
, AV_SAMPLE_FMT_NONE
};
122 static const enum AVPixelFormat pix_fmts
[] = { AV_PIX_FMT_RGBA
, AV_PIX_FMT_NONE
};
124 formats
= ff_make_format_list(sample_fmts
);
126 return AVERROR(ENOMEM
);
127 ff_formats_ref(formats
, &inlink
->out_formats
);
129 ff_add_channel_layout(&layout
, AV_CH_LAYOUT_STEREO
);
130 ff_channel_layouts_ref(layout
, &inlink
->out_channel_layouts
);
132 formats
= ff_all_samplerates();
134 return AVERROR(ENOMEM
);
135 ff_formats_ref(formats
, &inlink
->out_samplerates
);
137 formats
= ff_make_format_list(pix_fmts
);
139 return AVERROR(ENOMEM
);
140 ff_formats_ref(formats
, &outlink
->in_formats
);
145 static int config_input(AVFilterLink
*inlink
)
147 AVFilterContext
*ctx
= inlink
->dst
;
148 AudioVectorScopeContext
*p
= ctx
->priv
;
151 nb_samples
= FFMAX(1024, ((double)inlink
->sample_rate
/ av_q2d(p
->frame_rate
)) + 0.5);
152 inlink
->partial_buf_size
=
153 inlink
->min_samples
=
154 inlink
->max_samples
= nb_samples
;
159 static int config_output(AVFilterLink
*outlink
)
161 AudioVectorScopeContext
*p
= outlink
->src
->priv
;
165 outlink
->sample_aspect_ratio
= (AVRational
){1,1};
166 outlink
->frame_rate
= p
->frame_rate
;
174 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*insamples
)
176 AVFilterContext
*ctx
= inlink
->dst
;
177 AVFilterLink
*outlink
= ctx
->outputs
[0];
178 AudioVectorScopeContext
*p
= ctx
->priv
;
179 const int hw
= p
->hw
;
180 const int hh
= p
->hh
;
182 const double zoom
= p
->zoom
;
185 if (!p
->outpicref
|| p
->outpicref
->width
!= outlink
->w
||
186 p
->outpicref
->height
!= outlink
->h
) {
187 av_frame_free(&p
->outpicref
);
188 p
->outpicref
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
190 av_frame_free(&insamples
);
191 return AVERROR(ENOMEM
);
194 for (i
= 0; i
< outlink
->h
; i
++)
195 memset(p
->outpicref
->data
[0] + i
* p
->outpicref
->linesize
[0], 0, outlink
->w
* 4);
197 p
->outpicref
->pts
= insamples
->pts
;
201 switch (insamples
->format
) {
202 case AV_SAMPLE_FMT_S16
:
203 for (i
= 0; i
< insamples
->nb_samples
; i
++) {
204 int16_t *src
= (int16_t *)insamples
->data
[0] + i
* 2;
206 if (p
->mode
== LISSAJOUS
) {
207 x
= ((src
[1] - src
[0]) * zoom
/ (float)(UINT16_MAX
) + 1) * hw
;
208 y
= (1.0 - (src
[0] + src
[1]) * zoom
/ (float)UINT16_MAX
) * hh
;
210 x
= (src
[1] * zoom
/ (float)INT16_MAX
+ 1) * hw
;
211 y
= (src
[0] * zoom
/ (float)INT16_MAX
+ 1) * hh
;
217 case AV_SAMPLE_FMT_FLT
:
218 for (i
= 0; i
< insamples
->nb_samples
; i
++) {
219 float *src
= (float *)insamples
->data
[0] + i
* 2;
221 if (p
->mode
== LISSAJOUS
) {
222 x
= ((src
[1] - src
[0]) * zoom
/ 2 + 1) * hw
;
223 y
= (1.0 - (src
[0] + src
[1]) * zoom
/ 2) * hh
;
225 x
= (src
[1] * zoom
+ 1) * hw
;
226 y
= (src
[0] * zoom
+ 1) * hh
;
234 av_frame_free(&insamples
);
236 return ff_filter_frame(outlink
, av_frame_clone(p
->outpicref
));
239 static av_cold
void uninit(AVFilterContext
*ctx
)
241 AudioVectorScopeContext
*p
= ctx
->priv
;
243 av_frame_free(&p
->outpicref
);
246 static const AVFilterPad audiovectorscope_inputs
[] = {
249 .type
= AVMEDIA_TYPE_AUDIO
,
250 .config_props
= config_input
,
251 .filter_frame
= filter_frame
,
256 static const AVFilterPad audiovectorscope_outputs
[] = {
259 .type
= AVMEDIA_TYPE_VIDEO
,
260 .config_props
= config_output
,
265 AVFilter ff_avf_avectorscope
= {
266 .name
= "avectorscope",
267 .description
= NULL_IF_CONFIG_SMALL("Convert input audio to vectorscope video output."),
269 .query_formats
= query_formats
,
270 .priv_size
= sizeof(AudioVectorScopeContext
),
271 .inputs
= audiovectorscope_inputs
,
272 .outputs
= audiovectorscope_outputs
,
273 .priv_class
= &avectorscope_class
,