avfilter/avf_showwaves: color support
[ffmpeg.git] / 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 struct frame_node {
46     AVFrame *frame;
47     struct frame_node *next;
48 };
49
50 typedef struct {
51     const AVClass *class;
52     int w, h;
53     AVRational rate;
54     char *colors;
55     int buf_idx;
56     int16_t *buf_idy;    /* y coordinate of previous sample for each channel */
57     AVFrame *outpicref;
58     int n;
59     int pixstep;
60     int sample_count_mod;
61     int mode;                   ///< ShowWavesMode
62     int split_channels;
63     uint8_t *fg;
64     void (*draw_sample)(uint8_t *buf, int height, int linesize,
65                         int16_t sample, int16_t *prev_y, const uint8_t color[4]);
66
67     /* single picture */
68     int single_pic;
69     struct frame_node *audio_frames;
70     struct frame_node *last_frame;
71     int64_t total_samples;
72     int64_t *sum; /* abs sum of the samples per channel */
73 } ShowWavesContext;
74
75 #define OFFSET(x) offsetof(ShowWavesContext, x)
76 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
77
78 static const AVOption showwaves_options[] = {
79     { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "600x240"}, 0, 0, FLAGS },
80     { "s",    "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "600x240"}, 0, 0, FLAGS },
81     { "mode", "select display mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_POINT}, 0, MODE_NB-1, FLAGS, "mode"},
82         { "point", "draw a point for each sample",         0, AV_OPT_TYPE_CONST, {.i64=MODE_POINT},         .flags=FLAGS, .unit="mode"},
83         { "line",  "draw a line for each sample",          0, AV_OPT_TYPE_CONST, {.i64=MODE_LINE},          .flags=FLAGS, .unit="mode"},
84         { "p2p",   "draw a line between samples",          0, AV_OPT_TYPE_CONST, {.i64=MODE_P2P},           .flags=FLAGS, .unit="mode"},
85         { "cline", "draw a centered line for each sample", 0, AV_OPT_TYPE_CONST, {.i64=MODE_CENTERED_LINE}, .flags=FLAGS, .unit="mode"},
86     { "n",    "set how many samples to show in the same point", OFFSET(n), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, FLAGS },
87     { "rate", "set video rate", OFFSET(rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, FLAGS },
88     { "r",    "set video rate", OFFSET(rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, FLAGS },
89     { "split_channels", "draw channels separately", OFFSET(split_channels), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS },
90     { "colors", "set channels colors", OFFSET(colors), AV_OPT_TYPE_STRING, {.str = "red|green|blue|yellow|orange|lime|pink|magenta|brown" }, 0, 0, FLAGS },
91     { NULL }
92 };
93
94 AVFILTER_DEFINE_CLASS(showwaves);
95
96 static av_cold void uninit(AVFilterContext *ctx)
97 {
98     ShowWavesContext *showwaves = ctx->priv;
99
100     av_frame_free(&showwaves->outpicref);
101     av_freep(&showwaves->buf_idy);
102     av_freep(&showwaves->fg);
103
104     if (showwaves->single_pic) {
105         struct frame_node *node = showwaves->audio_frames;
106         while (node) {
107             struct frame_node *tmp = node;
108
109             node = node->next;
110             av_frame_free(&tmp->frame);
111             av_freep(&tmp);
112         }
113         av_freep(&showwaves->sum);
114         showwaves->last_frame = NULL;
115     }
116 }
117
118 static int query_formats(AVFilterContext *ctx)
119 {
120     AVFilterFormats *formats = NULL;
121     AVFilterChannelLayouts *layouts = NULL;
122     AVFilterLink *inlink = ctx->inputs[0];
123     AVFilterLink *outlink = ctx->outputs[0];
124     static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE };
125     static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_RGBA, AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE };
126     int ret;
127
128     /* set input audio formats */
129     formats = ff_make_format_list(sample_fmts);
130     if ((ret = ff_formats_ref(formats, &inlink->out_formats)) < 0)
131         return ret;
132
133     layouts = ff_all_channel_layouts();
134     if ((ret = ff_channel_layouts_ref(layouts, &inlink->out_channel_layouts)) < 0)
135         return ret;
136
137     formats = ff_all_samplerates();
138     if ((ret = ff_formats_ref(formats, &inlink->out_samplerates)) < 0)
139         return ret;
140
141     /* set output video format */
142     formats = ff_make_format_list(pix_fmts);
143     if ((ret = ff_formats_ref(formats, &outlink->in_formats)) < 0)
144         return ret;
145
146     return 0;
147 }
148
149 static void draw_sample_point_rgba(uint8_t *buf, int height, int linesize,
150                                    int16_t sample, int16_t *prev_y,
151                                    const uint8_t color[4])
152 {
153     const int h = height/2 - av_rescale(sample, height/2, INT16_MAX);
154     if (h >= 0 && h < height) {
155         buf[h * linesize + 0] += color[0];
156         buf[h * linesize + 1] += color[1];
157         buf[h * linesize + 2] += color[2];
158         buf[h * linesize + 3] += color[3];
159     }
160 }
161
162 static void draw_sample_line_rgba(uint8_t *buf, int height, int linesize,
163                                   int16_t sample, int16_t *prev_y,
164                                   const uint8_t color[4])
165 {
166     int k;
167     const int h = height/2 - av_rescale(sample, height/2, INT16_MAX);
168     int start   = height/2;
169     int end     = av_clip(h, 0, height-1);
170     if (start > end)
171         FFSWAP(int16_t, start, end);
172     for (k = start; k < end; k++) {
173         buf[k * linesize + 0] += color[0];
174         buf[k * linesize + 1] += color[1];
175         buf[k * linesize + 2] += color[2];
176         buf[k * linesize + 3] += color[3];
177     }
178 }
179
180 static void draw_sample_p2p_rgba(uint8_t *buf, int height, int linesize,
181                                  int16_t sample, int16_t *prev_y,
182                                  const uint8_t color[4])
183 {
184     int k;
185     const int h = height/2 - av_rescale(sample, height/2, INT16_MAX);
186     if (h >= 0 && h < height) {
187         buf[h * linesize + 0] += color[0];
188         buf[h * linesize + 1] += color[1];
189         buf[h * linesize + 2] += color[2];
190         buf[h * linesize + 3] += color[3];
191         if (*prev_y && h != *prev_y) {
192             int start = *prev_y;
193             int end = av_clip(h, 0, height-1);
194             if (start > end)
195                 FFSWAP(int16_t, start, end);
196             for (k = start + 1; k < end; k++) {
197                 buf[k * linesize + 0] += color[0];
198                 buf[k * linesize + 1] += color[1];
199                 buf[k * linesize + 2] += color[2];
200                 buf[k * linesize + 3] += color[3];
201             }
202         }
203     }
204     *prev_y = h;
205 }
206
207 static void draw_sample_cline_rgba(uint8_t *buf, int height, int linesize,
208                                    int16_t sample, int16_t *prev_y,
209                                    const uint8_t color[4])
210 {
211     int k;
212     const int h     = av_rescale(abs(sample), height, INT16_MAX);
213     const int start = (height - h) / 2;
214     const int end   = start + h;
215     for (k = start; k < end; k++) {
216         buf[k * linesize + 0] += color[0];
217         buf[k * linesize + 1] += color[1];
218         buf[k * linesize + 2] += color[2];
219         buf[k * linesize + 3] += color[3];
220     }
221 }
222
223 static void draw_sample_point_gray(uint8_t *buf, int height, int linesize,
224                                    int16_t sample, int16_t *prev_y,
225                                    const uint8_t color[4])
226 {
227     const int h = height/2 - av_rescale(sample, height/2, INT16_MAX);
228     if (h >= 0 && h < height)
229         buf[h * linesize] += color[0];
230 }
231
232 static void draw_sample_line_gray(uint8_t *buf, int height, int linesize,
233                                   int16_t sample, int16_t *prev_y,
234                                   const uint8_t color[4])
235 {
236     int k;
237     const int h = height/2 - av_rescale(sample, height/2, INT16_MAX);
238     int start   = height/2;
239     int end     = av_clip(h, 0, height-1);
240     if (start > end)
241         FFSWAP(int16_t, start, end);
242     for (k = start; k < end; k++)
243         buf[k * linesize] += color[0];
244 }
245
246 static void draw_sample_p2p_gray(uint8_t *buf, int height, int linesize,
247                                  int16_t sample, int16_t *prev_y,
248                                  const uint8_t color[4])
249 {
250     int k;
251     const int h = height/2 - av_rescale(sample, height/2, INT16_MAX);
252     if (h >= 0 && h < height) {
253         buf[h * linesize] += color[0];
254         if (*prev_y && h != *prev_y) {
255             int start = *prev_y;
256             int end = av_clip(h, 0, height-1);
257             if (start > end)
258                 FFSWAP(int16_t, start, end);
259             for (k = start + 1; k < end; k++)
260                 buf[k * linesize] += color[0];
261         }
262     }
263     *prev_y = h;
264 }
265
266 static void draw_sample_cline_gray(uint8_t *buf, int height, int linesize,
267                                    int16_t sample, int16_t *prev_y,
268                                    const uint8_t color[4])
269 {
270     int k;
271     const int h     = av_rescale(abs(sample), height, INT16_MAX);
272     const int start = (height - h) / 2;
273     const int end   = start + h;
274     for (k = start; k < end; k++)
275         buf[k * linesize] += color[0];
276 }
277
278 static int config_output(AVFilterLink *outlink)
279 {
280     AVFilterContext *ctx = outlink->src;
281     AVFilterLink *inlink = ctx->inputs[0];
282     ShowWavesContext *showwaves = ctx->priv;
283     int nb_channels = inlink->channels;
284     char *colors, *saveptr = NULL;
285     uint8_t x;
286     int ch;
287
288     if (showwaves->single_pic)
289         showwaves->n = 1;
290
291     if (!showwaves->n)
292         showwaves->n = FFMAX(1, ((double)inlink->sample_rate / (showwaves->w * av_q2d(showwaves->rate))) + 0.5);
293
294     showwaves->buf_idx = 0;
295     if (!(showwaves->buf_idy = av_mallocz_array(nb_channels, sizeof(*showwaves->buf_idy)))) {
296         av_log(ctx, AV_LOG_ERROR, "Could not allocate showwaves buffer\n");
297         return AVERROR(ENOMEM);
298     }
299     outlink->w = showwaves->w;
300     outlink->h = showwaves->h;
301     outlink->sample_aspect_ratio = (AVRational){1,1};
302
303     outlink->frame_rate = av_div_q((AVRational){inlink->sample_rate,showwaves->n},
304                                    (AVRational){showwaves->w,1});
305
306     av_log(ctx, AV_LOG_VERBOSE, "s:%dx%d r:%f n:%d\n",
307            showwaves->w, showwaves->h, av_q2d(outlink->frame_rate), showwaves->n);
308
309     switch (outlink->format) {
310     case AV_PIX_FMT_GRAY8:
311         switch (showwaves->mode) {
312         case MODE_POINT:         showwaves->draw_sample = draw_sample_point_gray; break;
313         case MODE_LINE:          showwaves->draw_sample = draw_sample_line_gray;  break;
314         case MODE_P2P:           showwaves->draw_sample = draw_sample_p2p_gray;   break;
315         case MODE_CENTERED_LINE: showwaves->draw_sample = draw_sample_cline_gray; break;
316         default:
317             return AVERROR_BUG;
318         }
319         showwaves->pixstep = 1;
320         break;
321     case AV_PIX_FMT_RGBA:
322         switch (showwaves->mode) {
323         case MODE_POINT:         showwaves->draw_sample = draw_sample_point_rgba; break;
324         case MODE_LINE:          showwaves->draw_sample = draw_sample_line_rgba;  break;
325         case MODE_P2P:           showwaves->draw_sample = draw_sample_p2p_rgba;   break;
326         case MODE_CENTERED_LINE: showwaves->draw_sample = draw_sample_cline_rgba; break;
327         default:
328             return AVERROR_BUG;
329         }
330         showwaves->pixstep = 4;
331         break;
332     }
333
334     showwaves->fg = av_malloc_array(nb_channels, 4 * sizeof(*showwaves->fg));
335     if (!showwaves->fg)
336         return AVERROR(ENOMEM);
337
338     colors = av_strdup(showwaves->colors);
339     if (!colors)
340         return AVERROR(ENOMEM);
341
342     /* multiplication factor, pre-computed to avoid in-loop divisions */
343     x = 255 / ((showwaves->split_channels ? 1 : nb_channels) * showwaves->n);
344     if (outlink->format == AV_PIX_FMT_RGBA) {
345         uint8_t fg[4] = { 0xff, 0xff, 0xff, 0xff };
346
347         for (ch = 0; ch < nb_channels; ch++) {
348             char *color;
349
350             color = av_strtok(ch == 0 ? colors : NULL, " |", &saveptr);
351             if (color)
352                 av_parse_color(fg, color, -1, ctx);
353             showwaves->fg[4*ch + 0] = fg[0] * x / 255.;
354             showwaves->fg[4*ch + 1] = fg[1] * x / 255.;
355             showwaves->fg[4*ch + 2] = fg[2] * x / 255.;
356             showwaves->fg[4*ch + 3] = fg[3] * x / 255.;
357         }
358     } else {
359         for (ch = 0; ch < nb_channels; ch++)
360             showwaves->fg[4 * ch + 0] = x;
361     }
362     av_free(colors);
363
364     return 0;
365 }
366
367 inline static int push_frame(AVFilterLink *outlink)
368 {
369     AVFilterContext *ctx = outlink->src;
370     AVFilterLink *inlink = ctx->inputs[0];
371     ShowWavesContext *showwaves = outlink->src->priv;
372     int nb_channels = inlink->channels;
373     int ret, i;
374
375     ret = ff_filter_frame(outlink, showwaves->outpicref);
376     showwaves->outpicref = NULL;
377     showwaves->buf_idx = 0;
378     for (i = 0; i < nb_channels; i++)
379         showwaves->buf_idy[i] = 0;
380     return ret;
381 }
382
383 static int push_single_pic(AVFilterLink *outlink)
384 {
385     AVFilterContext *ctx = outlink->src;
386     AVFilterLink *inlink = ctx->inputs[0];
387     ShowWavesContext *showwaves = ctx->priv;
388     int64_t n = 0, max_samples = showwaves->total_samples / outlink->w;
389     AVFrame *out = showwaves->outpicref;
390     struct frame_node *node;
391     const int nb_channels = inlink->channels;
392     const int ch_height = showwaves->split_channels ? outlink->h / nb_channels : outlink->h;
393     const int linesize = out->linesize[0];
394     const int pixstep = showwaves->pixstep;
395     int col = 0;
396     int64_t *sum = showwaves->sum;
397
398     if (max_samples == 0) {
399         av_log(ctx, AV_LOG_ERROR, "Too few samples\n");
400         return AVERROR(EINVAL);
401     }
402
403     av_log(ctx, AV_LOG_DEBUG, "Create frame averaging %"PRId64" samples per column\n", max_samples);
404
405     memset(sum, 0, nb_channels);
406
407     for (node = showwaves->audio_frames; node; node = node->next) {
408         int i;
409         const AVFrame *frame = node->frame;
410         const int16_t *p = (const int16_t *)frame->data[0];
411
412         for (i = 0; i < frame->nb_samples; i++) {
413             int ch;
414
415             for (ch = 0; ch < nb_channels; ch++)
416                 sum[ch] += abs(p[ch + i*nb_channels]) << 1;
417             if (n++ == max_samples) {
418                 for (ch = 0; ch < nb_channels; ch++) {
419                     int16_t sample = sum[ch] / max_samples;
420                     uint8_t *buf = out->data[0] + col * pixstep;
421                     if (showwaves->split_channels)
422                         buf += ch*ch_height*linesize;
423                     av_assert0(col < outlink->w);
424                     showwaves->draw_sample(buf, ch_height, linesize, sample, &showwaves->buf_idy[ch], &showwaves->fg[ch * 4]);
425                     sum[ch] = 0;
426                 }
427                 col++;
428                 n = 0;
429             }
430         }
431     }
432
433     return push_frame(outlink);
434 }
435
436
437 static int request_frame(AVFilterLink *outlink)
438 {
439     ShowWavesContext *showwaves = outlink->src->priv;
440     AVFilterLink *inlink = outlink->src->inputs[0];
441     int ret;
442
443     ret = ff_request_frame(inlink);
444     if (ret == AVERROR_EOF && showwaves->outpicref) {
445         if (showwaves->single_pic)
446             push_single_pic(outlink);
447         else
448             push_frame(outlink);
449     }
450
451     return ret;
452 }
453
454 static int alloc_out_frame(ShowWavesContext *showwaves, const int16_t *p,
455                            const AVFilterLink *inlink, AVFilterLink *outlink,
456                            const AVFrame *in)
457 {
458     if (!showwaves->outpicref) {
459         int j;
460         AVFrame *out = showwaves->outpicref =
461             ff_get_video_buffer(outlink, outlink->w, outlink->h);
462         if (!out)
463             return AVERROR(ENOMEM);
464         out->width  = outlink->w;
465         out->height = outlink->h;
466         out->pts = in->pts + av_rescale_q((p - (int16_t *)in->data[0]) / inlink->channels,
467                                           av_make_q(1, inlink->sample_rate),
468                                           outlink->time_base);
469         for (j = 0; j < outlink->h; j++)
470             memset(out->data[0] + j*out->linesize[0], 0, outlink->w * showwaves->pixstep);
471     }
472     return 0;
473 }
474
475 static av_cold int init(AVFilterContext *ctx)
476 {
477     ShowWavesContext *showwaves = ctx->priv;
478
479     if (!strcmp(ctx->filter->name, "showwavespic")) {
480         showwaves->single_pic = 1;
481         showwaves->mode = MODE_CENTERED_LINE;
482     }
483
484     return 0;
485 }
486
487 #if CONFIG_SHOWWAVES_FILTER
488
489 static int showwaves_filter_frame(AVFilterLink *inlink, AVFrame *insamples)
490 {
491     AVFilterContext *ctx = inlink->dst;
492     AVFilterLink *outlink = ctx->outputs[0];
493     ShowWavesContext *showwaves = ctx->priv;
494     const int nb_samples = insamples->nb_samples;
495     AVFrame *outpicref = showwaves->outpicref;
496     int16_t *p = (int16_t *)insamples->data[0];
497     int nb_channels = inlink->channels;
498     int i, j, ret = 0;
499     const int pixstep = showwaves->pixstep;
500     const int n = showwaves->n;
501     const int ch_height = showwaves->split_channels ? outlink->h / nb_channels : outlink->h;
502
503     /* draw data in the buffer */
504     for (i = 0; i < nb_samples; i++) {
505
506         ret = alloc_out_frame(showwaves, p, inlink, outlink, insamples);
507         if (ret < 0)
508             goto end;
509         outpicref = showwaves->outpicref;
510
511         for (j = 0; j < nb_channels; j++) {
512             uint8_t *buf = outpicref->data[0] + showwaves->buf_idx * pixstep;
513             const int linesize = outpicref->linesize[0];
514             if (showwaves->split_channels)
515                 buf += j*ch_height*linesize;
516             showwaves->draw_sample(buf, ch_height, linesize, *p++,
517                                    &showwaves->buf_idy[j], &showwaves->fg[j * 4]);
518         }
519
520         showwaves->sample_count_mod++;
521         if (showwaves->sample_count_mod == n) {
522             showwaves->sample_count_mod = 0;
523             showwaves->buf_idx++;
524         }
525         if (showwaves->buf_idx == showwaves->w)
526             if ((ret = push_frame(outlink)) < 0)
527                 break;
528         outpicref = showwaves->outpicref;
529     }
530
531 end:
532     av_frame_free(&insamples);
533     return ret;
534 }
535
536 static const AVFilterPad showwaves_inputs[] = {
537     {
538         .name         = "default",
539         .type         = AVMEDIA_TYPE_AUDIO,
540         .filter_frame = showwaves_filter_frame,
541     },
542     { NULL }
543 };
544
545 static const AVFilterPad showwaves_outputs[] = {
546     {
547         .name          = "default",
548         .type          = AVMEDIA_TYPE_VIDEO,
549         .config_props  = config_output,
550         .request_frame = request_frame,
551     },
552     { NULL }
553 };
554
555 AVFilter ff_avf_showwaves = {
556     .name          = "showwaves",
557     .description   = NULL_IF_CONFIG_SMALL("Convert input audio to a video output."),
558     .init          = init,
559     .uninit        = uninit,
560     .query_formats = query_formats,
561     .priv_size     = sizeof(ShowWavesContext),
562     .inputs        = showwaves_inputs,
563     .outputs       = showwaves_outputs,
564     .priv_class    = &showwaves_class,
565 };
566
567 #endif // CONFIG_SHOWWAVES_FILTER
568
569 #if CONFIG_SHOWWAVESPIC_FILTER
570
571 #define OFFSET(x) offsetof(ShowWavesContext, x)
572 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
573
574 static const AVOption showwavespic_options[] = {
575     { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "600x240"}, 0, 0, FLAGS },
576     { "s",    "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "600x240"}, 0, 0, FLAGS },
577     { "split_channels", "draw channels separately", OFFSET(split_channels), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS },
578     { "colors", "set channels colors", OFFSET(colors), AV_OPT_TYPE_STRING, {.str = "red|green|blue|yellow|orange|lime|pink|magenta|brown" }, 0, 0, FLAGS },
579     { NULL }
580 };
581
582 AVFILTER_DEFINE_CLASS(showwavespic);
583
584 static int showwavespic_config_input(AVFilterLink *inlink)
585 {
586     AVFilterContext *ctx = inlink->dst;
587     ShowWavesContext *showwaves = ctx->priv;
588
589     if (showwaves->single_pic) {
590         showwaves->sum = av_mallocz_array(inlink->channels, sizeof(*showwaves->sum));
591         if (!showwaves->sum)
592             return AVERROR(ENOMEM);
593     }
594
595     return 0;
596 }
597
598 static int showwavespic_filter_frame(AVFilterLink *inlink, AVFrame *insamples)
599 {
600     AVFilterContext *ctx = inlink->dst;
601     AVFilterLink *outlink = ctx->outputs[0];
602     ShowWavesContext *showwaves = ctx->priv;
603     int16_t *p = (int16_t *)insamples->data[0];
604     int ret = 0;
605
606     if (showwaves->single_pic) {
607         struct frame_node *f;
608
609         ret = alloc_out_frame(showwaves, p, inlink, outlink, insamples);
610         if (ret < 0)
611             goto end;
612
613         /* queue the audio frame */
614         f = av_malloc(sizeof(*f));
615         if (!f) {
616             ret = AVERROR(ENOMEM);
617             goto end;
618         }
619         f->frame = insamples;
620         f->next = NULL;
621         if (!showwaves->last_frame) {
622             showwaves->audio_frames =
623             showwaves->last_frame   = f;
624         } else {
625             showwaves->last_frame->next = f;
626             showwaves->last_frame = f;
627         }
628         showwaves->total_samples += insamples->nb_samples;
629
630         return 0;
631     }
632
633 end:
634     av_frame_free(&insamples);
635     return ret;
636 }
637
638 static const AVFilterPad showwavespic_inputs[] = {
639     {
640         .name         = "default",
641         .type         = AVMEDIA_TYPE_AUDIO,
642         .config_props = showwavespic_config_input,
643         .filter_frame = showwavespic_filter_frame,
644     },
645     { NULL }
646 };
647
648 static const AVFilterPad showwavespic_outputs[] = {
649     {
650         .name          = "default",
651         .type          = AVMEDIA_TYPE_VIDEO,
652         .config_props  = config_output,
653         .request_frame = request_frame,
654     },
655     { NULL }
656 };
657
658 AVFilter ff_avf_showwavespic = {
659     .name          = "showwavespic",
660     .description   = NULL_IF_CONFIG_SMALL("Convert input audio to a video output single picture."),
661     .init          = init,
662     .uninit        = uninit,
663     .query_formats = query_formats,
664     .priv_size     = sizeof(ShowWavesContext),
665     .inputs        = showwavespic_inputs,
666     .outputs       = showwavespic_outputs,
667     .priv_class    = &showwavespic_class,
668 };
669
670 #endif // CONFIG_SHOWWAVESPIC_FILTER