2 * Copyright (c) 2012 Stefano Sabatini
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 filter
26 #include "libavutil/channel_layout.h"
27 #include "libavutil/opt.h"
28 #include "libavutil/parseutils.h"
48 int16_t *buf_idy
; /* y coordinate of previous sample for each channel */
53 enum ShowWavesMode mode
;
55 void (*draw_sample
)(uint8_t *buf
, int height
, int linesize
,
56 int16_t sample
, int16_t *prev_y
, int intensity
);
59 #define OFFSET(x) offsetof(ShowWavesContext, x)
60 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
62 static const AVOption showwaves_options
[] = {
63 { "size", "set video size", OFFSET(w
), AV_OPT_TYPE_IMAGE_SIZE
, {.str
= "600x240"}, 0, 0, FLAGS
},
64 { "s", "set video size", OFFSET(w
), AV_OPT_TYPE_IMAGE_SIZE
, {.str
= "600x240"}, 0, 0, FLAGS
},
65 { "mode", "select display mode", OFFSET(mode
), AV_OPT_TYPE_INT
, {.i64
=MODE_POINT
}, 0, MODE_NB
-1, FLAGS
, "mode"},
66 { "point", "draw a point for each sample", 0, AV_OPT_TYPE_CONST
, {.i64
=MODE_POINT
}, .flags
=FLAGS
, .unit
="mode"},
67 { "line", "draw a line for each sample", 0, AV_OPT_TYPE_CONST
, {.i64
=MODE_LINE
}, .flags
=FLAGS
, .unit
="mode"},
68 { "p2p", "draw a line between samples", 0, AV_OPT_TYPE_CONST
, {.i64
=MODE_P2P
}, .flags
=FLAGS
, .unit
="mode"},
69 { "cline", "draw a centered line for each sample", 0, AV_OPT_TYPE_CONST
, {.i64
=MODE_CENTERED_LINE
}, .flags
=FLAGS
, .unit
="mode"},
70 { "n", "set how many samples to show in the same point", OFFSET(n
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, INT_MAX
, FLAGS
},
71 { "rate", "set video rate", OFFSET(rate
), AV_OPT_TYPE_VIDEO_RATE
, {.str
= "25"}, 0, 0, FLAGS
},
72 { "r", "set video rate", OFFSET(rate
), AV_OPT_TYPE_VIDEO_RATE
, {.str
= "25"}, 0, 0, FLAGS
},
73 { "split_channels", "draw channels separately", OFFSET(split_channels
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, 1, FLAGS
},
77 AVFILTER_DEFINE_CLASS(showwaves
);
79 static av_cold
void uninit(AVFilterContext
*ctx
)
81 ShowWavesContext
*showwaves
= ctx
->priv
;
83 av_frame_free(&showwaves
->outpicref
);
84 av_freep(&showwaves
->buf_idy
);
87 static int query_formats(AVFilterContext
*ctx
)
89 AVFilterFormats
*formats
= NULL
;
90 AVFilterChannelLayouts
*layouts
= NULL
;
91 AVFilterLink
*inlink
= ctx
->inputs
[0];
92 AVFilterLink
*outlink
= ctx
->outputs
[0];
93 static const enum AVSampleFormat sample_fmts
[] = { AV_SAMPLE_FMT_S16
, AV_SAMPLE_FMT_NONE
};
94 static const enum AVPixelFormat pix_fmts
[] = { AV_PIX_FMT_GRAY8
, AV_PIX_FMT_NONE
};
96 /* set input audio formats */
97 formats
= ff_make_format_list(sample_fmts
);
99 return AVERROR(ENOMEM
);
100 ff_formats_ref(formats
, &inlink
->out_formats
);
102 layouts
= ff_all_channel_layouts();
104 return AVERROR(ENOMEM
);
105 ff_channel_layouts_ref(layouts
, &inlink
->out_channel_layouts
);
107 formats
= ff_all_samplerates();
109 return AVERROR(ENOMEM
);
110 ff_formats_ref(formats
, &inlink
->out_samplerates
);
112 /* set output video format */
113 formats
= ff_make_format_list(pix_fmts
);
115 return AVERROR(ENOMEM
);
116 ff_formats_ref(formats
, &outlink
->in_formats
);
121 static int config_output(AVFilterLink
*outlink
)
123 AVFilterContext
*ctx
= outlink
->src
;
124 AVFilterLink
*inlink
= ctx
->inputs
[0];
125 ShowWavesContext
*showwaves
= ctx
->priv
;
126 int nb_channels
= inlink
->channels
;
129 showwaves
->n
= FFMAX(1, ((double)inlink
->sample_rate
/ (showwaves
->w
* av_q2d(showwaves
->rate
))) + 0.5);
131 showwaves
->buf_idx
= 0;
132 if (!(showwaves
->buf_idy
= av_mallocz_array(nb_channels
, sizeof(*showwaves
->buf_idy
)))) {
133 av_log(ctx
, AV_LOG_ERROR
, "Could not allocate showwaves buffer\n");
134 return AVERROR(ENOMEM
);
136 outlink
->w
= showwaves
->w
;
137 outlink
->h
= showwaves
->h
;
138 outlink
->sample_aspect_ratio
= (AVRational
){1,1};
140 outlink
->frame_rate
= av_div_q((AVRational
){inlink
->sample_rate
,showwaves
->n
},
141 (AVRational
){showwaves
->w
,1});
143 av_log(ctx
, AV_LOG_VERBOSE
, "s:%dx%d r:%f n:%d\n",
144 showwaves
->w
, showwaves
->h
, av_q2d(outlink
->frame_rate
), showwaves
->n
);
148 inline static int push_frame(AVFilterLink
*outlink
)
150 AVFilterContext
*ctx
= outlink
->src
;
151 AVFilterLink
*inlink
= ctx
->inputs
[0];
152 ShowWavesContext
*showwaves
= outlink
->src
->priv
;
153 int nb_channels
= inlink
->channels
;
156 if ((ret
= ff_filter_frame(outlink
, showwaves
->outpicref
)) >= 0)
157 showwaves
->req_fullfilled
= 1;
158 showwaves
->outpicref
= NULL
;
159 showwaves
->buf_idx
= 0;
160 for (i
= 0; i
< nb_channels
; i
++)
161 showwaves
->buf_idy
[i
] = 0;
165 static int request_frame(AVFilterLink
*outlink
)
167 ShowWavesContext
*showwaves
= outlink
->src
->priv
;
168 AVFilterLink
*inlink
= outlink
->src
->inputs
[0];
171 showwaves
->req_fullfilled
= 0;
173 ret
= ff_request_frame(inlink
);
174 } while (!showwaves
->req_fullfilled
&& ret
>= 0);
176 if (ret
== AVERROR_EOF
&& showwaves
->outpicref
)
181 #define MAX_INT16 ((1<<15) -1)
183 static void draw_sample_point(uint8_t *buf
, int height
, int linesize
,
184 int16_t sample
, int16_t *prev_y
, int intensity
)
186 const int h
= height
/2 - av_rescale(sample
, height
/2, MAX_INT16
);
187 if (h
>= 0 && h
< height
)
188 buf
[h
* linesize
] += intensity
;
191 static void draw_sample_line(uint8_t *buf
, int height
, int linesize
,
192 int16_t sample
, int16_t *prev_y
, int intensity
)
195 const int h
= height
/2 - av_rescale(sample
, height
/2, MAX_INT16
);
196 int start
= height
/2;
197 int end
= av_clip(h
, 0, height
-1);
199 FFSWAP(int16_t, start
, end
);
200 for (k
= start
; k
< end
; k
++)
201 buf
[k
* linesize
] += intensity
;
204 static void draw_sample_p2p(uint8_t *buf
, int height
, int linesize
,
205 int16_t sample
, int16_t *prev_y
, int intensity
)
208 const int h
= height
/2 - av_rescale(sample
, height
/2, MAX_INT16
);
209 if (h
>= 0 && h
< height
) {
210 buf
[h
* linesize
] += intensity
;
211 if (*prev_y
&& h
!= *prev_y
) {
213 int end
= av_clip(h
, 0, height
-1);
215 FFSWAP(int16_t, start
, end
);
216 for (k
= start
+ 1; k
< end
; k
++)
217 buf
[k
* linesize
] += intensity
;
223 static void draw_sample_cline(uint8_t *buf
, int height
, int linesize
,
224 int16_t sample
, int16_t *prev_y
, int intensity
)
227 const int h
= av_rescale(abs(sample
), height
, UINT16_MAX
);
228 const int start
= (height
- h
) / 2;
229 const int end
= start
+ h
;
230 for (k
= start
; k
< end
; k
++)
231 buf
[k
* linesize
] += intensity
;
234 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*insamples
)
236 AVFilterContext
*ctx
= inlink
->dst
;
237 AVFilterLink
*outlink
= ctx
->outputs
[0];
238 ShowWavesContext
*showwaves
= ctx
->priv
;
239 const int nb_samples
= insamples
->nb_samples
;
240 AVFrame
*outpicref
= showwaves
->outpicref
;
241 int linesize
= outpicref
? outpicref
->linesize
[0] : 0;
242 int16_t *p
= (int16_t *)insamples
->data
[0];
243 int nb_channels
= inlink
->channels
;
245 const int n
= showwaves
->n
;
246 const int x
= 255 / ((showwaves
->split_channels
? 1 : nb_channels
) * n
); /* multiplication factor, pre-computed to avoid in-loop divisions */
247 const int ch_height
= showwaves
->split_channels
? outlink
->h
/ nb_channels
: outlink
->h
;
249 /* draw data in the buffer */
250 for (i
= 0; i
< nb_samples
; i
++) {
251 if (!showwaves
->outpicref
) {
252 showwaves
->outpicref
= outpicref
=
253 ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
255 return AVERROR(ENOMEM
);
256 outpicref
->width
= outlink
->w
;
257 outpicref
->height
= outlink
->h
;
258 outpicref
->pts
= insamples
->pts
+
259 av_rescale_q((p
- (int16_t *)insamples
->data
[0]) / nb_channels
,
260 (AVRational
){ 1, inlink
->sample_rate
},
262 linesize
= outpicref
->linesize
[0];
263 for (j
= 0; j
< outlink
->h
; j
++)
264 memset(outpicref
->data
[0] + j
* linesize
, 0, outlink
->w
);
266 for (j
= 0; j
< nb_channels
; j
++) {
267 uint8_t *buf
= outpicref
->data
[0] + showwaves
->buf_idx
;
268 if (showwaves
->split_channels
)
269 buf
+= j
*ch_height
*linesize
;
270 showwaves
->draw_sample(buf
, ch_height
, linesize
, *p
++,
271 &showwaves
->buf_idy
[j
], x
);
274 showwaves
->sample_count_mod
++;
275 if (showwaves
->sample_count_mod
== n
) {
276 showwaves
->sample_count_mod
= 0;
277 showwaves
->buf_idx
++;
279 if (showwaves
->buf_idx
== showwaves
->w
)
280 if ((ret
= push_frame(outlink
)) < 0)
282 outpicref
= showwaves
->outpicref
;
285 av_frame_free(&insamples
);
289 static av_cold
int init(AVFilterContext
*ctx
)
291 ShowWavesContext
*showwaves
= ctx
->priv
;
293 switch (showwaves
->mode
) {
294 case MODE_POINT
: showwaves
->draw_sample
= draw_sample_point
; break;
295 case MODE_LINE
: showwaves
->draw_sample
= draw_sample_line
; break;
296 case MODE_P2P
: showwaves
->draw_sample
= draw_sample_p2p
; break;
297 case MODE_CENTERED_LINE
: showwaves
->draw_sample
= draw_sample_cline
; break;
304 static const AVFilterPad showwaves_inputs
[] = {
307 .type
= AVMEDIA_TYPE_AUDIO
,
308 .filter_frame
= filter_frame
,
313 static const AVFilterPad showwaves_outputs
[] = {
316 .type
= AVMEDIA_TYPE_VIDEO
,
317 .config_props
= config_output
,
318 .request_frame
= request_frame
,
323 AVFilter ff_avf_showwaves
= {
325 .description
= NULL_IF_CONFIG_SMALL("Convert input audio to a video output."),
328 .query_formats
= query_formats
,
329 .priv_size
= sizeof(ShowWavesContext
),
330 .inputs
= showwaves_inputs
,
331 .outputs
= showwaves_outputs
,
332 .priv_class
= &showwaves_class
,