avfilter: add waveform monitor filter
[ffmpeg.git] / libavfilter / vf_waveform.c
1 /*
2  * Copyright (c) 2012-2015 Paul B Mahol
3  * Copyright (c) 2013 Marton Balint
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * FFmpeg is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with FFmpeg; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21
22 #include "libavutil/avassert.h"
23 #include "libavutil/opt.h"
24 #include "libavutil/parseutils.h"
25 #include "libavutil/pixdesc.h"
26 #include "avfilter.h"
27 #include "formats.h"
28 #include "internal.h"
29 #include "video.h"
30
31 typedef struct WaveformContext {
32     const AVClass *class;
33     int            mode;
34     int            ncomp;
35     int            pcomp;
36     const uint8_t  *bg_color;
37     int            intensity;
38     int            mirror;
39     int            display;
40     int            envelope;
41     int            estart[4];
42     int            eend[4];
43     int            *emax[4];
44     int            *emin[4];
45     const AVPixFmtDescriptor *desc;
46 } WaveformContext;
47
48 #define OFFSET(x) offsetof(WaveformContext, x)
49 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
50
51 static const AVOption waveform_options[] = {
52     { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, "mode" },
53     { "m",    "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, "mode" },
54         { "row",    NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "mode" },
55         { "column", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "mode" },
56     { "intensity", "set intensity", OFFSET(intensity), AV_OPT_TYPE_INT, {.i64=10}, 1, 255, FLAGS },
57     { "i",         "set intensity", OFFSET(intensity), AV_OPT_TYPE_INT, {.i64=10}, 1, 255, FLAGS },
58     { "mirror", "set mirroring", OFFSET(mirror), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS },
59     { "r",      "set mirroring", OFFSET(mirror), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS },
60     { "display", "set display mode", OFFSET(display), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, "display" },
61     { "d",       "set display mode", OFFSET(display), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, "display" },
62         { "overlay", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "display" },
63         { "parade",  NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "display" },
64     { "components", "set components to display", OFFSET(pcomp), AV_OPT_TYPE_INT, {.i64=1}, 1, 15, FLAGS },
65     { "c",          "set components to display", OFFSET(pcomp), AV_OPT_TYPE_INT, {.i64=1}, 1, 15, FLAGS },
66     { "envelope", "set envelope to display", OFFSET(envelope), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, FLAGS, "envelope" },
67     { "e",        "set envelope to display", OFFSET(envelope), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, FLAGS, "envelope" },
68         { "none",         NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "envelope" },
69         { "instant",      NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "envelope" },
70         { "peak",         NULL, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "envelope" },
71         { "peak+instant", NULL, 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, FLAGS, "envelope" },
72     { NULL }
73 };
74
75 AVFILTER_DEFINE_CLASS(waveform);
76
77 static const enum AVPixelFormat pix_fmts[] = {
78      AV_PIX_FMT_GBRP,     AV_PIX_FMT_GBRAP,
79      AV_PIX_FMT_YUV422P,  AV_PIX_FMT_YUV420P,
80      AV_PIX_FMT_YUV444P,  AV_PIX_FMT_YUV440P,
81      AV_PIX_FMT_YUV411P,  AV_PIX_FMT_YUV410P,
82      AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUVJ420P,
83      AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P,
84      AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA420P,
85      AV_PIX_FMT_GRAY8,
86      AV_PIX_FMT_NONE
87 };
88
89 static int query_formats(AVFilterContext *ctx)
90 {
91     AVFilterFormats *fmts_list;
92
93     fmts_list = ff_make_format_list(pix_fmts);
94     if (!fmts_list)
95         return AVERROR(ENOMEM);
96     return ff_set_common_formats(ctx, fmts_list);
97 }
98
99 static const uint8_t black_yuva_color[4] = { 0, 127, 127, 255 };
100 static const uint8_t black_gbrp_color[4] = { 0, 0, 0, 255 };
101
102 static int config_input(AVFilterLink *inlink)
103 {
104     AVFilterContext *ctx = inlink->dst;
105     WaveformContext *s = ctx->priv;
106
107     s->desc  = av_pix_fmt_desc_get(inlink->format);
108     s->ncomp = s->desc->nb_components;
109
110     switch (inlink->format) {
111     case AV_PIX_FMT_GBRAP:
112     case AV_PIX_FMT_GBRP:
113         s->bg_color = black_gbrp_color;
114         break;
115     default:
116         s->bg_color = black_yuva_color;
117     }
118
119     return 0;
120 }
121
122 static int config_output(AVFilterLink *outlink)
123 {
124     AVFilterContext *ctx = outlink->src;
125     AVFilterLink *inlink = ctx->inputs[0];
126     WaveformContext *s = ctx->priv;
127     int comp = 0, i, j = 0, p, size, shift;
128
129     for (i = 0; i < s->ncomp; i++) {
130         if ((1 << i) & s->pcomp)
131             comp++;
132     }
133
134     for (p = 0; p < 4; p++) {
135         av_freep(&s->emax[p]);
136         av_freep(&s->emin[p]);
137     }
138
139     if (s->mode) {
140         outlink->h = 256 * FFMAX(comp * s->display, 1);
141         size = inlink->w * sizeof(int);
142     } else {
143         outlink->w = 256 * FFMAX(comp * s->display, 1);
144         size = inlink->h * sizeof(int);
145     }
146
147     for (p = 0; p < 4; p++) {
148         const int is_chroma = (p == 1 || p == 2);
149         const int shift_w = (is_chroma ? s->desc->log2_chroma_w : 0);
150         const int shift_h = (is_chroma ? s->desc->log2_chroma_h : 0);
151         const int plane = s->desc->comp[p].plane;
152         int offset;
153
154         if (!((1 << p) & s->pcomp))
155             continue;
156
157         shift = s->mode ? shift_h : shift_w;
158
159         s->emax[plane] = av_malloc(size);
160         s->emin[plane] = av_malloc(size);
161
162         if (!s->emin[plane] || !s->emax[plane])
163             return AVERROR(ENOMEM);
164
165         offset = j++ * 256 * s->display;
166         s->estart[plane] = offset >> shift;
167         s->eend[plane]   = (offset + 255) >> shift;
168         for (i = 0; i < size / sizeof(int); i++) {
169             s->emax[plane][i] = s->estart[plane];
170             s->emin[plane][i] = s->eend[plane];
171         }
172     }
173
174     outlink->sample_aspect_ratio = (AVRational){1,1};
175
176     return 0;
177 }
178
179 static void gen_waveform(WaveformContext *s, AVFrame *in, AVFrame *out,
180                          int component, int intensity, int offset, int col_mode)
181 {
182     const int plane = s->desc->comp[component].plane;
183     const int mirror = s->mirror;
184     const int is_chroma = (component == 1 || component == 2);
185     const int shift_w = (is_chroma ? s->desc->log2_chroma_w : 0);
186     const int shift_h = (is_chroma ? s->desc->log2_chroma_h : 0);
187     const int src_linesize = in->linesize[plane];
188     const int dst_linesize = out->linesize[plane];
189     const int dst_signed_linesize = dst_linesize * (mirror == 1 ? -1 : 1);
190     const int max = 255 - intensity;
191     const int src_h = FF_CEIL_RSHIFT(in->height, shift_h);
192     const int src_w = FF_CEIL_RSHIFT(in->width, shift_w);
193     const uint8_t *src_data = in->data[plane];
194     uint8_t *dst_data = out->data[plane] + (col_mode ? (offset >> shift_h) * dst_linesize : offset >> shift_w);
195     uint8_t * const dst_bottom_line = dst_data + dst_linesize * ((256 >> shift_h) - 1);
196     uint8_t * const dst_line = (mirror ? dst_bottom_line : dst_data);
197     const uint8_t *p;
198     uint8_t *dst;
199     int y;
200
201     if (!col_mode && mirror)
202         dst_data += 256 >> shift_w;
203     for (y = 0; y < src_h; y++) {
204         const uint8_t *src_data_end = src_data + src_w;
205         dst = dst_line;
206         for (p = src_data; p < src_data_end; p++) {
207             uint8_t *target;
208             if (col_mode) {
209                 target = dst++ + dst_signed_linesize * (*p >> shift_h);
210             } else {
211                 if (mirror)
212                     target = dst_data - (*p >> shift_w) - 1;
213                 else
214                     target = dst_data + (*p >> shift_w);
215             }
216             if (*target <= max)
217                 *target += intensity;
218             else
219                 *target = 255;
220         }
221         src_data += src_linesize;
222         dst_data += dst_linesize;
223     }
224 }
225
226 static void gen_envelope_instant(WaveformContext *s, AVFrame *out, int component)
227 {
228     const int plane = s->desc->comp[component].plane;
229     const int dst_linesize = out->linesize[plane];
230     const uint8_t bg = s->bg_color[plane];
231     const int is_chroma = (component == 1 || component == 2);
232     const int shift_w = (is_chroma ? s->desc->log2_chroma_w : 0);
233     const int shift_h = (is_chroma ? s->desc->log2_chroma_h : 0);
234     const int dst_h = FF_CEIL_RSHIFT(out->height, shift_h);
235     const int dst_w = FF_CEIL_RSHIFT(out->width, shift_w);
236     const int start = s->estart[plane];
237     const int end = s->eend[plane];
238     uint8_t *dst;
239     int x, y;
240
241     if (!s->mode) {
242         for (y = 0; y < dst_h; y++) {
243             dst = out->data[plane] + y * dst_linesize;
244             for (x = start; x < end; x++) {
245                 if (dst[x] != bg) {
246                     dst[x] = 255;
247                     break;
248                 }
249             }
250             for (x = end - 1; x >= start; x--) {
251                 if (dst[x] != bg) {
252                     dst[x] = 255;
253                     break;
254                 }
255             }
256         }
257     } else {
258         for (x = 0; x < dst_w; x++) {
259             for (y = start; y < end; y++) {
260                 dst = out->data[plane] + y * dst_linesize + x;
261                 if (dst[0] != bg) {
262                     dst[0] = 255;
263                     break;
264                 }
265             }
266             for (y = end - 1; y >= start; y--) {
267                 dst = out->data[plane] + y * dst_linesize + x;
268                 if (dst[0] != bg) {
269                     dst[0] = 255;
270                     break;
271                 }
272             }
273         }
274     }
275 }
276
277 static void gen_envelope_peak(WaveformContext *s, AVFrame *out, int component)
278 {
279     const int plane = s->desc->comp[component].plane;
280     const int dst_linesize = out->linesize[plane];
281     const uint8_t bg = s->bg_color[plane];
282     const int is_chroma = (component == 1 || component == 2);
283     const int shift_w = (is_chroma ? s->desc->log2_chroma_w : 0);
284     const int shift_h = (is_chroma ? s->desc->log2_chroma_h : 0);
285     const int dst_h = FF_CEIL_RSHIFT(out->height, shift_h);
286     const int dst_w = FF_CEIL_RSHIFT(out->width, shift_w);
287     const int start = s->estart[plane];
288     const int end = s->eend[plane];
289     int *emax = s->emax[plane];
290     int *emin = s->emin[plane];
291     uint8_t *dst;
292     int x, y;
293
294     if (!s->mode) {
295         for (y = 0; y < dst_h; y++) {
296             dst = out->data[plane] + y * dst_linesize;
297             for (x = start; x < end && x < emin[y]; x++) {
298                 if (dst[x] != bg) {
299                     emin[y] = x;
300                     break;
301                 }
302             }
303             for (x = end - 1; x >= start && x >= emax[y]; x--) {
304                 if (dst[x] != bg) {
305                     emax[y] = x;
306                     break;
307                 }
308             }
309         }
310
311         if (s->envelope == 3)
312             gen_envelope_instant(s, out, component);
313
314         for (y = 0; y < dst_h; y++) {
315             dst = out->data[plane] + y * dst_linesize + emin[y];
316             dst[0] = 255;
317             dst = out->data[plane] + y * dst_linesize + emax[y];
318             dst[0] = 255;
319         }
320     } else {
321         for (x = 0; x < dst_w; x++) {
322             for (y = start; y < end && y < emin[x]; y++) {
323                 dst = out->data[plane] + y * dst_linesize + x;
324                 if (dst[0] != bg) {
325                     emin[x] = y;
326                     break;
327                 }
328             }
329             for (y = end - 1; y >= start && y >= emax[x]; y--) {
330                 dst = out->data[plane] + y * dst_linesize + x;
331                 if (dst[0] != bg) {
332                     emax[x] = y;
333                     break;
334                 }
335             }
336         }
337
338         if (s->envelope == 3)
339             gen_envelope_instant(s, out, component);
340
341         for (x = 0; x < dst_w; x++) {
342             dst = out->data[plane] + emin[x] * dst_linesize + x;
343             dst[0] = 255;
344             dst = out->data[plane] + emax[x] * dst_linesize + x;
345             dst[0] = 255;
346         }
347     }
348 }
349
350 static void gen_envelope(WaveformContext *s, AVFrame *out, int component)
351 {
352     if (s->envelope == 0) {
353         return;
354     } else if (s->envelope == 1) {
355         gen_envelope_instant(s, out, component);
356     } else {
357         gen_envelope_peak(s, out, component);
358     }
359 }
360
361 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
362 {
363     AVFilterContext *ctx  = inlink->dst;
364     WaveformContext *s    = ctx->priv;
365     AVFilterLink *outlink = ctx->outputs[0];
366     AVFrame *out;
367     int i,  k;
368
369     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
370     if (!out) {
371         av_frame_free(&in);
372         return AVERROR(ENOMEM);
373     }
374     out->pts = in->pts;
375
376     for (k = 0; k < s->ncomp; k++) {
377         const int is_chroma = (k == 1 || k == 2);
378         const int dst_h = FF_CEIL_RSHIFT(outlink->h, (is_chroma ? s->desc->log2_chroma_h : 0));
379         const int dst_w = FF_CEIL_RSHIFT(outlink->w, (is_chroma ? s->desc->log2_chroma_w : 0));
380         for (i = 0; i < dst_h ; i++)
381             memset(out->data[s->desc->comp[k].plane] +
382                    i * out->linesize[s->desc->comp[k].plane],
383                    s->bg_color[k], dst_w);
384     }
385
386     for (k = 0, i = 0; k < s->ncomp; k++) {
387         if ((1 << k) & s->pcomp) {
388             const int offset = i++ * 256 * s->display;
389             gen_waveform(s, in, out, k, s->intensity, offset, s->mode);
390             gen_envelope(s, out, k);
391         }
392     }
393
394     av_frame_free(&in);
395     return ff_filter_frame(outlink, out);
396 }
397
398 static av_cold void uninit(AVFilterContext *ctx)
399 {
400     WaveformContext *s = ctx->priv;
401     int p;
402
403     for (p = 0; p < 4; p++) {
404         av_freep(&s->emax[p]);
405         av_freep(&s->emin[p]);
406     }
407 }
408
409 static const AVFilterPad inputs[] = {
410     {
411         .name         = "default",
412         .type         = AVMEDIA_TYPE_VIDEO,
413         .filter_frame = filter_frame,
414         .config_props = config_input,
415     },
416     { NULL }
417 };
418
419 static const AVFilterPad outputs[] = {
420     {
421         .name         = "default",
422         .type         = AVMEDIA_TYPE_VIDEO,
423         .config_props = config_output,
424     },
425     { NULL }
426 };
427
428 AVFilter ff_vf_waveform = {
429     .name          = "waveform",
430     .description   = NULL_IF_CONFIG_SMALL("Video waveform monitor."),
431     .priv_size     = sizeof(WaveformContext),
432     .priv_class    = &waveform_class,
433     .query_formats = query_formats,
434     .uninit        = uninit,
435     .inputs        = inputs,
436     .outputs       = outputs,
437 };