]> git.sesse.net Git - ffmpeg/blob - libavfilter/avf_showwaves.c
avfilter/avf_showwaves: Add draw mode also to showwavespic filter
[ffmpeg] / libavfilter / avf_showwaves.c
1 /*
2  * Copyright (c) 2012 Stefano Sabatini
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 /**
22  * @file
23  * audio to video multimedia filter
24  */
25
26 #include "libavutil/avassert.h"
27 #include "libavutil/avstring.h"
28 #include "libavutil/channel_layout.h"
29 #include "libavutil/opt.h"
30 #include "libavutil/parseutils.h"
31 #include "avfilter.h"
32 #include "formats.h"
33 #include "audio.h"
34 #include "video.h"
35 #include "internal.h"
36
37 enum ShowWavesMode {
38     MODE_POINT,
39     MODE_LINE,
40     MODE_P2P,
41     MODE_CENTERED_LINE,
42     MODE_NB,
43 };
44
45 enum ShowWavesScale {
46     SCALE_LIN,
47     SCALE_LOG,
48     SCALE_SQRT,
49     SCALE_CBRT,
50     SCALE_NB,
51 };
52
53 enum ShowWavesDrawMode {
54     DRAW_SCALE,
55     DRAW_FULL,
56     DRAW_NB,
57 };
58
59 struct frame_node {
60     AVFrame *frame;
61     struct frame_node *next;
62 };
63
64 typedef struct ShowWavesContext {
65     const AVClass *class;
66     int w, h;
67     AVRational rate;
68     char *colors;
69     int buf_idx;
70     int16_t *buf_idy;    /* y coordinate of previous sample for each channel */
71     AVFrame *outpicref;
72     int n;
73     int pixstep;
74     int sample_count_mod;
75     int mode;                   ///< ShowWavesMode
76     int scale;                  ///< ShowWavesScale
77     int draw_mode;              ///< ShowWavesDrawMode
78     int split_channels;
79     uint8_t *fg;
80
81     int (*get_h)(int16_t sample, int height);
82     void (*draw_sample)(uint8_t *buf, int height, int linesize,
83                         int16_t *prev_y, const uint8_t color[4], int h);
84
85     /* single picture */
86     int single_pic;
87     struct frame_node *audio_frames;
88     struct frame_node *last_frame;
89     int64_t total_samples;
90     int64_t *sum; /* abs sum of the samples per channel */
91 } ShowWavesContext;
92
93 #define OFFSET(x) offsetof(ShowWavesContext, x)
94 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
95
96 static const AVOption showwaves_options[] = {
97     { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "600x240"}, 0, 0, FLAGS },
98     { "s",    "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "600x240"}, 0, 0, FLAGS },
99     { "mode", "select display mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_POINT}, 0, MODE_NB-1, FLAGS, "mode"},
100         { "point", "draw a point for each sample",         0, AV_OPT_TYPE_CONST, {.i64=MODE_POINT},         .flags=FLAGS, .unit="mode"},
101         { "line",  "draw a line for each sample",          0, AV_OPT_TYPE_CONST, {.i64=MODE_LINE},          .flags=FLAGS, .unit="mode"},
102         { "p2p",   "draw a line between samples",          0, AV_OPT_TYPE_CONST, {.i64=MODE_P2P},           .flags=FLAGS, .unit="mode"},
103         { "cline", "draw a centered line for each sample", 0, AV_OPT_TYPE_CONST, {.i64=MODE_CENTERED_LINE}, .flags=FLAGS, .unit="mode"},
104     { "n",    "set how many samples to show in the same point", OFFSET(n), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, FLAGS },
105     { "rate", "set video rate", OFFSET(rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, FLAGS },
106     { "r",    "set video rate", OFFSET(rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, FLAGS },
107     { "split_channels", "draw channels separately", OFFSET(split_channels), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS },
108     { "colors", "set channels colors", OFFSET(colors), AV_OPT_TYPE_STRING, {.str = "red|green|blue|yellow|orange|lime|pink|magenta|brown" }, 0, 0, FLAGS },
109     { "scale", "set amplitude scale", OFFSET(scale), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, SCALE_NB-1, FLAGS, .unit="scale" },
110         { "lin", "linear",         0, AV_OPT_TYPE_CONST, {.i64=SCALE_LIN}, .flags=FLAGS, .unit="scale"},
111         { "log", "logarithmic",    0, AV_OPT_TYPE_CONST, {.i64=SCALE_LOG}, .flags=FLAGS, .unit="scale"},
112         { "sqrt", "square root",   0, AV_OPT_TYPE_CONST, {.i64=SCALE_SQRT}, .flags=FLAGS, .unit="scale"},
113         { "cbrt", "cubic root",    0, AV_OPT_TYPE_CONST, {.i64=SCALE_CBRT}, .flags=FLAGS, .unit="scale"},
114     { "draw", "set draw mode", OFFSET(draw_mode), AV_OPT_TYPE_INT, {.i64 = DRAW_SCALE}, 0, DRAW_NB-1, FLAGS, .unit="draw" },
115         { "scale", "scale pixel values for each drawn sample", 0, AV_OPT_TYPE_CONST, {.i64=DRAW_SCALE}, .flags=FLAGS, .unit="draw"},
116         { "full",  "draw every pixel for sample directly",     0, AV_OPT_TYPE_CONST, {.i64=DRAW_FULL},  .flags=FLAGS, .unit="draw"},
117     { NULL }
118 };
119
120 AVFILTER_DEFINE_CLASS(showwaves);
121
122 static av_cold void uninit(AVFilterContext *ctx)
123 {
124     ShowWavesContext *showwaves = ctx->priv;
125
126     av_frame_free(&showwaves->outpicref);
127     av_freep(&showwaves->buf_idy);
128     av_freep(&showwaves->fg);
129
130     if (showwaves->single_pic) {
131         struct frame_node *node = showwaves->audio_frames;
132         while (node) {
133             struct frame_node *tmp = node;
134
135             node = node->next;
136             av_frame_free(&tmp->frame);
137             av_freep(&tmp);
138         }
139         av_freep(&showwaves->sum);
140         showwaves->last_frame = NULL;
141     }
142 }
143
144 static int query_formats(AVFilterContext *ctx)
145 {
146     AVFilterFormats *formats = NULL;
147     AVFilterChannelLayouts *layouts = NULL;
148     AVFilterLink *inlink = ctx->inputs[0];
149     AVFilterLink *outlink = ctx->outputs[0];
150     static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE };
151     static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_RGBA, AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE };
152     int ret;
153
154     /* set input audio formats */
155     formats = ff_make_format_list(sample_fmts);
156     if ((ret = ff_formats_ref(formats, &inlink->out_formats)) < 0)
157         return ret;
158
159     layouts = ff_all_channel_layouts();
160     if ((ret = ff_channel_layouts_ref(layouts, &inlink->out_channel_layouts)) < 0)
161         return ret;
162
163     formats = ff_all_samplerates();
164     if ((ret = ff_formats_ref(formats, &inlink->out_samplerates)) < 0)
165         return ret;
166
167     /* set output video format */
168     formats = ff_make_format_list(pix_fmts);
169     if ((ret = ff_formats_ref(formats, &outlink->in_formats)) < 0)
170         return ret;
171
172     return 0;
173 }
174
175 static int get_lin_h(int16_t sample, int height)
176 {
177     return height/2 - av_rescale(sample, height/2, INT16_MAX);
178 }
179
180 static int get_lin_h2(int16_t sample, int height)
181 {
182     return av_rescale(FFABS(sample), height, INT16_MAX);
183 }
184
185 static int get_log_h(int16_t sample, int height)
186 {
187     return height/2 - FFSIGN(sample) * (log10(1 + FFABS(sample)) * (height/2) / log10(1 + INT16_MAX));
188 }
189
190 static int get_log_h2(int16_t sample, int height)
191 {
192     return log10(1 + FFABS(sample)) * height / log10(1 + INT16_MAX);
193 }
194
195 static int get_sqrt_h(int16_t sample, int height)
196 {
197     return height/2 - FFSIGN(sample) * (sqrt(FFABS(sample)) * (height/2) / sqrt(INT16_MAX));
198 }
199
200 static int get_sqrt_h2(int16_t sample, int height)
201 {
202     return sqrt(FFABS(sample)) * height / sqrt(INT16_MAX);
203 }
204
205 static int get_cbrt_h(int16_t sample, int height)
206 {
207     return height/2 - FFSIGN(sample) * (cbrt(FFABS(sample)) * (height/2) / cbrt(INT16_MAX));
208 }
209
210 static int get_cbrt_h2(int16_t sample, int height)
211 {
212     return cbrt(FFABS(sample)) * height / cbrt(INT16_MAX);
213 }
214
215 static void draw_sample_point_rgba_scale(uint8_t *buf, int height, int linesize,
216                                          int16_t *prev_y,
217                                          const uint8_t color[4], int h)
218 {
219     if (h >= 0 && h < height) {
220         buf[h * linesize + 0] += color[0];
221         buf[h * linesize + 1] += color[1];
222         buf[h * linesize + 2] += color[2];
223         buf[h * linesize + 3] += color[3];
224     }
225 }
226
227 static void draw_sample_point_rgba_full(uint8_t *buf, int height, int linesize,
228                                    int16_t *prev_y,
229                                    const uint8_t color[4], int h)
230 {
231     if (h >= 0 && h < height) {
232         buf[h * linesize + 0] = color[0];
233         buf[h * linesize + 1] = color[1];
234         buf[h * linesize + 2] = color[2];
235         buf[h * linesize + 3] = color[3];
236     }
237 }
238
239 static void draw_sample_line_rgba_scale(uint8_t *buf, int height, int linesize,
240                                         int16_t *prev_y,
241                                         const uint8_t color[4], int h)
242 {
243     int k;
244     int start   = height/2;
245     int end     = av_clip(h, 0, height-1);
246     if (start > end)
247         FFSWAP(int16_t, start, end);
248     for (k = start; k < end; k++) {
249         buf[k * linesize + 0] += color[0];
250         buf[k * linesize + 1] += color[1];
251         buf[k * linesize + 2] += color[2];
252         buf[k * linesize + 3] += color[3];
253     }
254 }
255
256 static void draw_sample_line_rgba_full(uint8_t *buf, int height, int linesize,
257                                        int16_t *prev_y,
258                                        const uint8_t color[4], int h)
259 {
260     int k;
261     int start   = height/2;
262     int end     = av_clip(h, 0, height-1);
263     if (start > end)
264         FFSWAP(int16_t, start, end);
265     for (k = start; k < end; k++) {
266         buf[k * linesize + 0] = color[0];
267         buf[k * linesize + 1] = color[1];
268         buf[k * linesize + 2] = color[2];
269         buf[k * linesize + 3] = color[3];
270     }
271 }
272
273 static void draw_sample_p2p_rgba_scale(uint8_t *buf, int height, int linesize,
274                                        int16_t *prev_y,
275                                        const uint8_t color[4], int h)
276 {
277     int k;
278     if (h >= 0 && h < height) {
279         buf[h * linesize + 0] += color[0];
280         buf[h * linesize + 1] += color[1];
281         buf[h * linesize + 2] += color[2];
282         buf[h * linesize + 3] += color[3];
283         if (*prev_y && h != *prev_y) {
284             int start = *prev_y;
285             int end = av_clip(h, 0, height-1);
286             if (start > end)
287                 FFSWAP(int16_t, start, end);
288             for (k = start + 1; k < end; k++) {
289                 buf[k * linesize + 0] += color[0];
290                 buf[k * linesize + 1] += color[1];
291                 buf[k * linesize + 2] += color[2];
292                 buf[k * linesize + 3] += color[3];
293             }
294         }
295     }
296     *prev_y = h;
297 }
298
299 static void draw_sample_p2p_rgba_full(uint8_t *buf, int height, int linesize,
300                                       int16_t *prev_y,
301                                       const uint8_t color[4], int h)
302 {
303     int k;
304     if (h >= 0 && h < height) {
305         buf[h * linesize + 0] = color[0];
306         buf[h * linesize + 1] = color[1];
307         buf[h * linesize + 2] = color[2];
308         buf[h * linesize + 3] = color[3];
309         if (*prev_y && h != *prev_y) {
310             int start = *prev_y;
311             int end = av_clip(h, 0, height-1);
312             if (start > end)
313                 FFSWAP(int16_t, start, end);
314             for (k = start + 1; k < end; k++) {
315                 buf[k * linesize + 0] = color[0];
316                 buf[k * linesize + 1] = color[1];
317                 buf[k * linesize + 2] = color[2];
318                 buf[k * linesize + 3] = color[3];
319             }
320         }
321     }
322     *prev_y = h;
323 }
324
325 static void draw_sample_cline_rgba_scale(uint8_t *buf, int height, int linesize,
326                                          int16_t *prev_y,
327                                          const uint8_t color[4], int h)
328 {
329     int k;
330     const int start = (height - h) / 2;
331     const int end   = start + h;
332     for (k = start; k < end; k++) {
333         buf[k * linesize + 0] += color[0];
334         buf[k * linesize + 1] += color[1];
335         buf[k * linesize + 2] += color[2];
336         buf[k * linesize + 3] += color[3];
337     }
338 }
339  static void draw_sample_cline_rgba_full(uint8_t *buf, int height, int linesize,
340                                          int16_t *prev_y,
341                                          const uint8_t color[4], int h)
342 {
343     int k;
344     const int start = (height - h) / 2;
345     const int end   = start + h;
346     for (k = start; k < end; k++) {
347         buf[k * linesize + 0] = color[0];
348         buf[k * linesize + 1] = color[1];
349         buf[k * linesize + 2] = color[2];
350         buf[k * linesize + 3] = color[3];
351     }
352 }
353
354 static void draw_sample_point_gray(uint8_t *buf, int height, int linesize,
355                                    int16_t *prev_y,
356                                    const uint8_t color[4], int h)
357 {
358     if (h >= 0 && h < height)
359         buf[h * linesize] += color[0];
360 }
361
362 static void draw_sample_line_gray(uint8_t *buf, int height, int linesize,
363                                   int16_t *prev_y,
364                                   const uint8_t color[4], int h)
365 {
366     int k;
367     int start   = height/2;
368     int end     = av_clip(h, 0, height-1);
369     if (start > end)
370         FFSWAP(int16_t, start, end);
371     for (k = start; k < end; k++)
372         buf[k * linesize] += color[0];
373 }
374
375 static void draw_sample_p2p_gray(uint8_t *buf, int height, int linesize,
376                                  int16_t *prev_y,
377                                  const uint8_t color[4], int h)
378 {
379     int k;
380     if (h >= 0 && h < height) {
381         buf[h * linesize] += color[0];
382         if (*prev_y && h != *prev_y) {
383             int start = *prev_y;
384             int end = av_clip(h, 0, height-1);
385             if (start > end)
386                 FFSWAP(int16_t, start, end);
387             for (k = start + 1; k < end; k++)
388                 buf[k * linesize] += color[0];
389         }
390     }
391     *prev_y = h;
392 }
393
394 static void draw_sample_cline_gray(uint8_t *buf, int height, int linesize,
395                                    int16_t *prev_y,
396                                    const uint8_t color[4], int h)
397 {
398     int k;
399     const int start = (height - h) / 2;
400     const int end   = start + h;
401     for (k = start; k < end; k++)
402         buf[k * linesize] += color[0];
403 }
404
405 static int config_output(AVFilterLink *outlink)
406 {
407     AVFilterContext *ctx = outlink->src;
408     AVFilterLink *inlink = ctx->inputs[0];
409     ShowWavesContext *showwaves = ctx->priv;
410     int nb_channels = inlink->channels;
411     char *colors, *saveptr = NULL;
412     uint8_t x;
413     int ch;
414
415     if (showwaves->single_pic)
416         showwaves->n = 1;
417
418     if (!showwaves->n)
419         showwaves->n = FFMAX(1, ((double)inlink->sample_rate / (showwaves->w * av_q2d(showwaves->rate))) + 0.5);
420
421     showwaves->buf_idx = 0;
422     if (!(showwaves->buf_idy = av_mallocz_array(nb_channels, sizeof(*showwaves->buf_idy)))) {
423         av_log(ctx, AV_LOG_ERROR, "Could not allocate showwaves buffer\n");
424         return AVERROR(ENOMEM);
425     }
426     outlink->w = showwaves->w;
427     outlink->h = showwaves->h;
428     outlink->sample_aspect_ratio = (AVRational){1,1};
429
430     outlink->frame_rate = av_div_q((AVRational){inlink->sample_rate,showwaves->n},
431                                    (AVRational){showwaves->w,1});
432
433     av_log(ctx, AV_LOG_VERBOSE, "s:%dx%d r:%f n:%d\n",
434            showwaves->w, showwaves->h, av_q2d(outlink->frame_rate), showwaves->n);
435
436     switch (outlink->format) {
437     case AV_PIX_FMT_GRAY8:
438         switch (showwaves->mode) {
439         case MODE_POINT:         showwaves->draw_sample = draw_sample_point_gray; break;
440         case MODE_LINE:          showwaves->draw_sample = draw_sample_line_gray;  break;
441         case MODE_P2P:           showwaves->draw_sample = draw_sample_p2p_gray;   break;
442         case MODE_CENTERED_LINE: showwaves->draw_sample = draw_sample_cline_gray; break;
443         default:
444             return AVERROR_BUG;
445         }
446         showwaves->pixstep = 1;
447         break;
448     case AV_PIX_FMT_RGBA:
449         switch (showwaves->mode) {
450         case MODE_POINT:         showwaves->draw_sample = showwaves->draw_mode == DRAW_SCALE ? draw_sample_point_rgba_scale : draw_sample_point_rgba_full; break;
451         case MODE_LINE:          showwaves->draw_sample = showwaves->draw_mode == DRAW_SCALE ? draw_sample_line_rgba_scale  : draw_sample_line_rgba_full;  break;
452         case MODE_P2P:           showwaves->draw_sample = showwaves->draw_mode == DRAW_SCALE ? draw_sample_p2p_rgba_scale   : draw_sample_p2p_rgba_full;   break;
453         case MODE_CENTERED_LINE: showwaves->draw_sample = showwaves->draw_mode == DRAW_SCALE ? draw_sample_cline_rgba_scale : draw_sample_cline_rgba_full; break;
454         default:
455             return AVERROR_BUG;
456         }
457         showwaves->pixstep = 4;
458         break;
459     }
460
461     switch (showwaves->scale) {
462     case SCALE_LIN:
463         switch (showwaves->mode) {
464         case MODE_POINT:
465         case MODE_LINE:
466         case MODE_P2P:           showwaves->get_h = get_lin_h;  break;
467         case MODE_CENTERED_LINE: showwaves->get_h = get_lin_h2; break;
468         default:
469             return AVERROR_BUG;
470         }
471         break;
472     case SCALE_LOG:
473         switch (showwaves->mode) {
474         case MODE_POINT:
475         case MODE_LINE:
476         case MODE_P2P:           showwaves->get_h = get_log_h;  break;
477         case MODE_CENTERED_LINE: showwaves->get_h = get_log_h2; break;
478         default:
479             return AVERROR_BUG;
480         }
481         break;
482     case SCALE_SQRT:
483         switch (showwaves->mode) {
484         case MODE_POINT:
485         case MODE_LINE:
486         case MODE_P2P:           showwaves->get_h = get_sqrt_h;  break;
487         case MODE_CENTERED_LINE: showwaves->get_h = get_sqrt_h2; break;
488         default:
489             return AVERROR_BUG;
490         }
491         break;
492     case SCALE_CBRT:
493         switch (showwaves->mode) {
494         case MODE_POINT:
495         case MODE_LINE:
496         case MODE_P2P:           showwaves->get_h = get_cbrt_h;  break;
497         case MODE_CENTERED_LINE: showwaves->get_h = get_cbrt_h2; break;
498         default:
499             return AVERROR_BUG;
500         }
501         break;
502     }
503
504     showwaves->fg = av_malloc_array(nb_channels, 4 * sizeof(*showwaves->fg));
505     if (!showwaves->fg)
506         return AVERROR(ENOMEM);
507
508     colors = av_strdup(showwaves->colors);
509     if (!colors)
510         return AVERROR(ENOMEM);
511
512     if (showwaves->draw_mode == DRAW_SCALE) {
513         /* multiplication factor, pre-computed to avoid in-loop divisions */
514         x = 255 / ((showwaves->split_channels ? 1 : nb_channels) * showwaves->n);
515     } else {
516         x = 255;
517     }
518     if (outlink->format == AV_PIX_FMT_RGBA) {
519         uint8_t fg[4] = { 0xff, 0xff, 0xff, 0xff };
520
521         for (ch = 0; ch < nb_channels; ch++) {
522             char *color;
523
524             color = av_strtok(ch == 0 ? colors : NULL, " |", &saveptr);
525             if (color)
526                 av_parse_color(fg, color, -1, ctx);
527             showwaves->fg[4*ch + 0] = fg[0] * x / 255.;
528             showwaves->fg[4*ch + 1] = fg[1] * x / 255.;
529             showwaves->fg[4*ch + 2] = fg[2] * x / 255.;
530             showwaves->fg[4*ch + 3] = fg[3] * x / 255.;
531         }
532     } else {
533         for (ch = 0; ch < nb_channels; ch++)
534             showwaves->fg[4 * ch + 0] = x;
535     }
536     av_free(colors);
537
538     return 0;
539 }
540
541 inline static int push_frame(AVFilterLink *outlink)
542 {
543     AVFilterContext *ctx = outlink->src;
544     AVFilterLink *inlink = ctx->inputs[0];
545     ShowWavesContext *showwaves = outlink->src->priv;
546     int nb_channels = inlink->channels;
547     int ret, i;
548
549     ret = ff_filter_frame(outlink, showwaves->outpicref);
550     showwaves->outpicref = NULL;
551     showwaves->buf_idx = 0;
552     for (i = 0; i < nb_channels; i++)
553         showwaves->buf_idy[i] = 0;
554     return ret;
555 }
556
557 static int push_single_pic(AVFilterLink *outlink)
558 {
559     AVFilterContext *ctx = outlink->src;
560     AVFilterLink *inlink = ctx->inputs[0];
561     ShowWavesContext *showwaves = ctx->priv;
562     int64_t n = 0, column_max_samples = showwaves->total_samples / outlink->w;
563     int64_t remaining_samples = showwaves->total_samples - (column_max_samples * outlink->w);
564     int64_t last_column_samples = column_max_samples + remaining_samples;
565     AVFrame *out = showwaves->outpicref;
566     struct frame_node *node;
567     const int nb_channels = inlink->channels;
568     const int ch_height = showwaves->split_channels ? outlink->h / nb_channels : outlink->h;
569     const int linesize = out->linesize[0];
570     const int pixstep = showwaves->pixstep;
571     int col = 0;
572     int64_t *sum = showwaves->sum;
573
574     if (column_max_samples == 0) {
575         av_log(ctx, AV_LOG_ERROR, "Too few samples\n");
576         return AVERROR(EINVAL);
577     }
578
579     av_log(ctx, AV_LOG_DEBUG, "Create frame averaging %"PRId64" samples per column\n", column_max_samples);
580
581     memset(sum, 0, nb_channels);
582
583     for (node = showwaves->audio_frames; node; node = node->next) {
584         int i;
585         const AVFrame *frame = node->frame;
586         const int16_t *p = (const int16_t *)frame->data[0];
587
588         for (i = 0; i < frame->nb_samples; i++) {
589             int64_t max_samples = col == outlink->w - 1 ? last_column_samples: column_max_samples;
590             int ch;
591
592             for (ch = 0; ch < nb_channels; ch++)
593                 sum[ch] += abs(p[ch + i*nb_channels]) << 1;
594             n++;
595             if (n == max_samples) {
596                 for (ch = 0; ch < nb_channels; ch++) {
597                     int16_t sample = sum[ch] / max_samples;
598                     uint8_t *buf = out->data[0] + col * pixstep;
599                     int h;
600
601                     if (showwaves->split_channels)
602                         buf += ch*ch_height*linesize;
603                     av_assert0(col < outlink->w);
604                     h = showwaves->get_h(sample, ch_height);
605                     showwaves->draw_sample(buf, ch_height, linesize, &showwaves->buf_idy[ch], &showwaves->fg[ch * 4], h);
606                     sum[ch] = 0;
607                 }
608                 col++;
609                 n = 0;
610             }
611         }
612     }
613
614     return push_frame(outlink);
615 }
616
617
618 static int request_frame(AVFilterLink *outlink)
619 {
620     ShowWavesContext *showwaves = outlink->src->priv;
621     AVFilterLink *inlink = outlink->src->inputs[0];
622     int ret;
623
624     ret = ff_request_frame(inlink);
625     if (ret == AVERROR_EOF && showwaves->outpicref) {
626         if (showwaves->single_pic)
627             push_single_pic(outlink);
628         else
629             push_frame(outlink);
630     }
631
632     return ret;
633 }
634
635 static int alloc_out_frame(ShowWavesContext *showwaves, const int16_t *p,
636                            const AVFilterLink *inlink, AVFilterLink *outlink,
637                            const AVFrame *in)
638 {
639     if (!showwaves->outpicref) {
640         int j;
641         AVFrame *out = showwaves->outpicref =
642             ff_get_video_buffer(outlink, outlink->w, outlink->h);
643         if (!out)
644             return AVERROR(ENOMEM);
645         out->width  = outlink->w;
646         out->height = outlink->h;
647         out->pts = in->pts + av_rescale_q((p - (int16_t *)in->data[0]) / inlink->channels,
648                                           av_make_q(1, inlink->sample_rate),
649                                           outlink->time_base);
650         for (j = 0; j < outlink->h; j++)
651             memset(out->data[0] + j*out->linesize[0], 0, outlink->w * showwaves->pixstep);
652     }
653     return 0;
654 }
655
656 static av_cold int init(AVFilterContext *ctx)
657 {
658     ShowWavesContext *showwaves = ctx->priv;
659
660     if (!strcmp(ctx->filter->name, "showwavespic")) {
661         showwaves->single_pic = 1;
662         showwaves->mode = MODE_CENTERED_LINE;
663     }
664
665     return 0;
666 }
667
668 #if CONFIG_SHOWWAVES_FILTER
669
670 static int showwaves_filter_frame(AVFilterLink *inlink, AVFrame *insamples)
671 {
672     AVFilterContext *ctx = inlink->dst;
673     AVFilterLink *outlink = ctx->outputs[0];
674     ShowWavesContext *showwaves = ctx->priv;
675     const int nb_samples = insamples->nb_samples;
676     AVFrame *outpicref = showwaves->outpicref;
677     int16_t *p = (int16_t *)insamples->data[0];
678     int nb_channels = inlink->channels;
679     int i, j, ret = 0;
680     const int pixstep = showwaves->pixstep;
681     const int n = showwaves->n;
682     const int ch_height = showwaves->split_channels ? outlink->h / nb_channels : outlink->h;
683
684     /* draw data in the buffer */
685     for (i = 0; i < nb_samples; i++) {
686
687         ret = alloc_out_frame(showwaves, p, inlink, outlink, insamples);
688         if (ret < 0)
689             goto end;
690         outpicref = showwaves->outpicref;
691
692         for (j = 0; j < nb_channels; j++) {
693             uint8_t *buf = outpicref->data[0] + showwaves->buf_idx * pixstep;
694             const int linesize = outpicref->linesize[0];
695             int h;
696
697             if (showwaves->split_channels)
698                 buf += j*ch_height*linesize;
699             h = showwaves->get_h(*p++, ch_height);
700             showwaves->draw_sample(buf, ch_height, linesize,
701                                    &showwaves->buf_idy[j], &showwaves->fg[j * 4], h);
702         }
703
704         showwaves->sample_count_mod++;
705         if (showwaves->sample_count_mod == n) {
706             showwaves->sample_count_mod = 0;
707             showwaves->buf_idx++;
708         }
709         if (showwaves->buf_idx == showwaves->w)
710             if ((ret = push_frame(outlink)) < 0)
711                 break;
712         outpicref = showwaves->outpicref;
713     }
714
715 end:
716     av_frame_free(&insamples);
717     return ret;
718 }
719
720 static const AVFilterPad showwaves_inputs[] = {
721     {
722         .name         = "default",
723         .type         = AVMEDIA_TYPE_AUDIO,
724         .filter_frame = showwaves_filter_frame,
725     },
726     { NULL }
727 };
728
729 static const AVFilterPad showwaves_outputs[] = {
730     {
731         .name          = "default",
732         .type          = AVMEDIA_TYPE_VIDEO,
733         .config_props  = config_output,
734         .request_frame = request_frame,
735     },
736     { NULL }
737 };
738
739 AVFilter ff_avf_showwaves = {
740     .name          = "showwaves",
741     .description   = NULL_IF_CONFIG_SMALL("Convert input audio to a video output."),
742     .init          = init,
743     .uninit        = uninit,
744     .query_formats = query_formats,
745     .priv_size     = sizeof(ShowWavesContext),
746     .inputs        = showwaves_inputs,
747     .outputs       = showwaves_outputs,
748     .priv_class    = &showwaves_class,
749 };
750
751 #endif // CONFIG_SHOWWAVES_FILTER
752
753 #if CONFIG_SHOWWAVESPIC_FILTER
754
755 #define OFFSET(x) offsetof(ShowWavesContext, x)
756 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
757
758 static const AVOption showwavespic_options[] = {
759     { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "600x240"}, 0, 0, FLAGS },
760     { "s",    "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "600x240"}, 0, 0, FLAGS },
761     { "split_channels", "draw channels separately", OFFSET(split_channels), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS },
762     { "colors", "set channels colors", OFFSET(colors), AV_OPT_TYPE_STRING, {.str = "red|green|blue|yellow|orange|lime|pink|magenta|brown" }, 0, 0, FLAGS },
763     { "scale", "set amplitude scale", OFFSET(scale), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, SCALE_NB-1, FLAGS, .unit="scale" },
764         { "lin", "linear",         0, AV_OPT_TYPE_CONST, {.i64=SCALE_LIN}, .flags=FLAGS, .unit="scale"},
765         { "log", "logarithmic",    0, AV_OPT_TYPE_CONST, {.i64=SCALE_LOG}, .flags=FLAGS, .unit="scale"},
766         { "sqrt", "square root",   0, AV_OPT_TYPE_CONST, {.i64=SCALE_SQRT}, .flags=FLAGS, .unit="scale"},
767         { "cbrt", "cubic root",    0, AV_OPT_TYPE_CONST, {.i64=SCALE_CBRT}, .flags=FLAGS, .unit="scale"},
768     { "draw", "set draw mode", OFFSET(draw_mode), AV_OPT_TYPE_INT, {.i64 = DRAW_SCALE}, 0, DRAW_NB-1, FLAGS, .unit="draw" },
769         { "scale", "scale pixel values for each drawn sample", 0, AV_OPT_TYPE_CONST, {.i64=DRAW_SCALE}, .flags=FLAGS, .unit="draw"},
770         { "full",  "draw every pixel for sample directly",     0, AV_OPT_TYPE_CONST, {.i64=DRAW_FULL},  .flags=FLAGS, .unit="draw"},
771     { NULL }
772 };
773
774 AVFILTER_DEFINE_CLASS(showwavespic);
775
776 static int showwavespic_config_input(AVFilterLink *inlink)
777 {
778     AVFilterContext *ctx = inlink->dst;
779     ShowWavesContext *showwaves = ctx->priv;
780
781     if (showwaves->single_pic) {
782         showwaves->sum = av_mallocz_array(inlink->channels, sizeof(*showwaves->sum));
783         if (!showwaves->sum)
784             return AVERROR(ENOMEM);
785     }
786
787     return 0;
788 }
789
790 static int showwavespic_filter_frame(AVFilterLink *inlink, AVFrame *insamples)
791 {
792     AVFilterContext *ctx = inlink->dst;
793     AVFilterLink *outlink = ctx->outputs[0];
794     ShowWavesContext *showwaves = ctx->priv;
795     int16_t *p = (int16_t *)insamples->data[0];
796     int ret = 0;
797
798     if (showwaves->single_pic) {
799         struct frame_node *f;
800
801         ret = alloc_out_frame(showwaves, p, inlink, outlink, insamples);
802         if (ret < 0)
803             goto end;
804
805         /* queue the audio frame */
806         f = av_malloc(sizeof(*f));
807         if (!f) {
808             ret = AVERROR(ENOMEM);
809             goto end;
810         }
811         f->frame = insamples;
812         f->next = NULL;
813         if (!showwaves->last_frame) {
814             showwaves->audio_frames =
815             showwaves->last_frame   = f;
816         } else {
817             showwaves->last_frame->next = f;
818             showwaves->last_frame = f;
819         }
820         showwaves->total_samples += insamples->nb_samples;
821
822         return 0;
823     }
824
825 end:
826     av_frame_free(&insamples);
827     return ret;
828 }
829
830 static const AVFilterPad showwavespic_inputs[] = {
831     {
832         .name         = "default",
833         .type         = AVMEDIA_TYPE_AUDIO,
834         .config_props = showwavespic_config_input,
835         .filter_frame = showwavespic_filter_frame,
836     },
837     { NULL }
838 };
839
840 static const AVFilterPad showwavespic_outputs[] = {
841     {
842         .name          = "default",
843         .type          = AVMEDIA_TYPE_VIDEO,
844         .config_props  = config_output,
845         .request_frame = request_frame,
846     },
847     { NULL }
848 };
849
850 AVFilter ff_avf_showwavespic = {
851     .name          = "showwavespic",
852     .description   = NULL_IF_CONFIG_SMALL("Convert input audio to a video output single picture."),
853     .init          = init,
854     .uninit        = uninit,
855     .query_formats = query_formats,
856     .priv_size     = sizeof(ShowWavesContext),
857     .inputs        = showwavespic_inputs,
858     .outputs       = showwavespic_outputs,
859     .priv_class    = &showwavespic_class,
860 };
861
862 #endif // CONFIG_SHOWWAVESPIC_FILTER