]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_signalstats.c
avfilter/vf_signalstats: add SignalstatsContext.maxsize variable
[ffmpeg] / libavfilter / vf_signalstats.c
1 /*
2  * Copyright (c) 2010 Mark Heath mjpeg0 @ silicontrip dot org
3  * Copyright (c) 2014 Clément Bœsch
4  * Copyright (c) 2014 Dave Rice @dericed
5  *
6  * This file is part of FFmpeg.
7  *
8  * FFmpeg is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * FFmpeg is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with FFmpeg; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22
23 #include "libavutil/intreadwrite.h"
24 #include "libavutil/opt.h"
25 #include "libavutil/pixdesc.h"
26 #include "internal.h"
27
28 enum FilterMode {
29     FILTER_NONE = -1,
30     FILTER_TOUT,
31     FILTER_VREP,
32     FILTER_BRNG,
33     FILT_NUMB
34 };
35
36 typedef struct SignalstatsContext {
37     const AVClass *class;
38     int chromah;    // height of chroma plane
39     int chromaw;    // width of chroma plane
40     int hsub;       // horizontal subsampling
41     int vsub;       // vertical subsampling
42     int depth;      // pixel depth
43     int fs;         // pixel count per frame
44     int cfs;        // pixel count per frame of chroma planes
45     int outfilter;  // FilterMode
46     int filters;
47     AVFrame *frame_prev;
48     uint8_t rgba_color[4];
49     int yuv_color[3];
50     int nb_jobs;
51     int *jobs_rets;
52
53     int maxsize;    // history stats array size
54     int *histy, *histu, *histv, *histsat;
55
56     AVFrame *frame_sat;
57     AVFrame *frame_hue;
58 } SignalstatsContext;
59
60 typedef struct ThreadData {
61     const AVFrame *in;
62     AVFrame *out;
63 } ThreadData;
64
65 typedef struct ThreadDataHueSatMetrics {
66     const AVFrame *src;
67     AVFrame *dst_sat, *dst_hue;
68 } ThreadDataHueSatMetrics;
69
70 #define OFFSET(x) offsetof(SignalstatsContext, x)
71 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
72
73 static const AVOption signalstats_options[] = {
74     {"stat", "set statistics filters", OFFSET(filters), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, INT_MAX, FLAGS, "filters"},
75         {"tout", "analyze pixels for temporal outliers",                0, AV_OPT_TYPE_CONST, {.i64=1<<FILTER_TOUT}, 0, 0, FLAGS, "filters"},
76         {"vrep", "analyze video lines for vertical line repetition",    0, AV_OPT_TYPE_CONST, {.i64=1<<FILTER_VREP}, 0, 0, FLAGS, "filters"},
77         {"brng", "analyze for pixels outside of broadcast range",       0, AV_OPT_TYPE_CONST, {.i64=1<<FILTER_BRNG}, 0, 0, FLAGS, "filters"},
78     {"out", "set video filter", OFFSET(outfilter), AV_OPT_TYPE_INT, {.i64=FILTER_NONE}, -1, FILT_NUMB-1, FLAGS, "out"},
79         {"tout", "highlight pixels that depict temporal outliers",              0, AV_OPT_TYPE_CONST, {.i64=FILTER_TOUT}, 0, 0, FLAGS, "out"},
80         {"vrep", "highlight video lines that depict vertical line repetition",  0, AV_OPT_TYPE_CONST, {.i64=FILTER_VREP}, 0, 0, FLAGS, "out"},
81         {"brng", "highlight pixels that are outside of broadcast range",        0, AV_OPT_TYPE_CONST, {.i64=FILTER_BRNG}, 0, 0, FLAGS, "out"},
82     {"c",     "set highlight color", OFFSET(rgba_color), AV_OPT_TYPE_COLOR, {.str="yellow"}, .flags=FLAGS},
83     {"color", "set highlight color", OFFSET(rgba_color), AV_OPT_TYPE_COLOR, {.str="yellow"}, .flags=FLAGS},
84     {NULL}
85 };
86
87 AVFILTER_DEFINE_CLASS(signalstats);
88
89 static av_cold int init(AVFilterContext *ctx)
90 {
91     uint8_t r, g, b;
92     SignalstatsContext *s = ctx->priv;
93
94     if (s->outfilter != FILTER_NONE)
95         s->filters |= 1 << s->outfilter;
96
97     r = s->rgba_color[0];
98     g = s->rgba_color[1];
99     b = s->rgba_color[2];
100     s->yuv_color[0] = (( 66*r + 129*g +  25*b + (1<<7)) >> 8) +  16;
101     s->yuv_color[1] = ((-38*r + -74*g + 112*b + (1<<7)) >> 8) + 128;
102     s->yuv_color[2] = ((112*r + -94*g + -18*b + (1<<7)) >> 8) + 128;
103     return 0;
104 }
105
106 static av_cold void uninit(AVFilterContext *ctx)
107 {
108     SignalstatsContext *s = ctx->priv;
109     av_frame_free(&s->frame_prev);
110     av_frame_free(&s->frame_sat);
111     av_frame_free(&s->frame_hue);
112     av_freep(&s->jobs_rets);
113     av_freep(&s->histy);
114     av_freep(&s->histu);
115     av_freep(&s->histv);
116     av_freep(&s->histsat);
117 }
118
119 static int query_formats(AVFilterContext *ctx)
120 {
121     // TODO: add more
122     static const enum AVPixelFormat pix_fmts[] = {
123         AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV411P,
124         AV_PIX_FMT_YUV440P,
125         AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ411P,
126         AV_PIX_FMT_YUVJ440P,
127         AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV420P9,
128         AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV420P10,
129         AV_PIX_FMT_YUV440P10,
130         AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV420P12,
131         AV_PIX_FMT_YUV440P12,
132         AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV420P14,
133         AV_PIX_FMT_YUV444P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV420P16,
134         AV_PIX_FMT_NONE
135     };
136
137     AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
138     if (!fmts_list)
139         return AVERROR(ENOMEM);
140     return ff_set_common_formats(ctx, fmts_list);
141 }
142
143 static AVFrame *alloc_frame(enum AVPixelFormat pixfmt, int w, int h)
144 {
145     AVFrame *frame = av_frame_alloc();
146     if (!frame)
147         return NULL;
148
149     frame->format = pixfmt;
150     frame->width  = w;
151     frame->height = h;
152
153     if (av_frame_get_buffer(frame, 32) < 0) {
154         av_frame_free(&frame);
155         return NULL;
156     }
157
158     return frame;
159 }
160
161 static int config_output(AVFilterLink *outlink)
162 {
163     AVFilterContext *ctx = outlink->src;
164     SignalstatsContext *s = ctx->priv;
165     AVFilterLink *inlink = outlink->src->inputs[0];
166     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format);
167     s->hsub = desc->log2_chroma_w;
168     s->vsub = desc->log2_chroma_h;
169     s->depth = desc->comp[0].depth;
170     s->maxsize = 1 << s->depth;
171     if (s->depth > 8) {
172         s->histy = av_malloc_array(s->maxsize, sizeof(*s->histy));
173         s->histu = av_malloc_array(s->maxsize, sizeof(*s->histu));
174         s->histv = av_malloc_array(s->maxsize, sizeof(*s->histv));
175         s->histsat = av_malloc_array(s->maxsize, sizeof(*s->histsat));
176
177         if (!s->histy || !s->histu || !s->histv || !s->histsat)
178             return AVERROR(ENOMEM);
179     }
180
181     outlink->w = inlink->w;
182     outlink->h = inlink->h;
183
184     s->chromaw = AV_CEIL_RSHIFT(inlink->w, s->hsub);
185     s->chromah = AV_CEIL_RSHIFT(inlink->h, s->vsub);
186
187     s->fs = inlink->w * inlink->h;
188     s->cfs = s->chromaw * s->chromah;
189
190     s->nb_jobs   = FFMAX(1, FFMIN(inlink->h, ff_filter_get_nb_threads(ctx)));
191     s->jobs_rets = av_malloc_array(s->nb_jobs, sizeof(*s->jobs_rets));
192     if (!s->jobs_rets)
193         return AVERROR(ENOMEM);
194
195     s->frame_sat = alloc_frame(s->depth > 8 ? AV_PIX_FMT_GRAY16 : AV_PIX_FMT_GRAY8,  inlink->w, inlink->h);
196     s->frame_hue = alloc_frame(AV_PIX_FMT_GRAY16, inlink->w, inlink->h);
197     if (!s->frame_sat || !s->frame_hue)
198         return AVERROR(ENOMEM);
199
200     return 0;
201 }
202
203 static void burn_frame8(const SignalstatsContext *s, AVFrame *f, int x, int y)
204 {
205     const int chromax = x >> s->hsub;
206     const int chromay = y >> s->vsub;
207     f->data[0][y       * f->linesize[0] +       x] = s->yuv_color[0];
208     f->data[1][chromay * f->linesize[1] + chromax] = s->yuv_color[1];
209     f->data[2][chromay * f->linesize[2] + chromax] = s->yuv_color[2];
210 }
211
212 static void burn_frame16(const SignalstatsContext *s, AVFrame *f, int x, int y)
213 {
214     const int chromax = x >> s->hsub;
215     const int chromay = y >> s->vsub;
216     const int mult = 1 << (s->depth - 8);
217     AV_WN16(f->data[0] + y       * f->linesize[0] +       x * 2, s->yuv_color[0] * mult);
218     AV_WN16(f->data[1] + chromay * f->linesize[1] + chromax * 2, s->yuv_color[1] * mult);
219     AV_WN16(f->data[2] + chromay * f->linesize[2] + chromax * 2, s->yuv_color[2] * mult);
220 }
221
222 static int filter8_brng(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
223 {
224     ThreadData *td = arg;
225     const SignalstatsContext *s = ctx->priv;
226     const AVFrame *in = td->in;
227     AVFrame *out = td->out;
228     const int w = in->width;
229     const int h = in->height;
230     const int slice_start = (h *  jobnr   ) / nb_jobs;
231     const int slice_end   = (h * (jobnr+1)) / nb_jobs;
232     int x, y, score = 0;
233
234     for (y = slice_start; y < slice_end; y++) {
235         const int yc = y >> s->vsub;
236         const uint8_t *pluma    = &in->data[0][y  * in->linesize[0]];
237         const uint8_t *pchromau = &in->data[1][yc * in->linesize[1]];
238         const uint8_t *pchromav = &in->data[2][yc * in->linesize[2]];
239
240         for (x = 0; x < w; x++) {
241             const int xc = x >> s->hsub;
242             const int luma    = pluma[x];
243             const int chromau = pchromau[xc];
244             const int chromav = pchromav[xc];
245             const int filt = luma    < 16 || luma    > 235 ||
246                 chromau < 16 || chromau > 240 ||
247                 chromav < 16 || chromav > 240;
248             score += filt;
249             if (out && filt)
250                 burn_frame8(s, out, x, y);
251         }
252     }
253     return score;
254 }
255
256 static int filter16_brng(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
257 {
258     ThreadData *td = arg;
259     const SignalstatsContext *s = ctx->priv;
260     const AVFrame *in = td->in;
261     AVFrame *out = td->out;
262     const int mult = 1 << (s->depth - 8);
263     const int w = in->width;
264     const int h = in->height;
265     const int slice_start = (h *  jobnr   ) / nb_jobs;
266     const int slice_end   = (h * (jobnr+1)) / nb_jobs;
267     int x, y, score = 0;
268
269     for (y = slice_start; y < slice_end; y++) {
270         const int yc = y >> s->vsub;
271         const uint16_t *pluma    = (uint16_t *)&in->data[0][y  * in->linesize[0]];
272         const uint16_t *pchromau = (uint16_t *)&in->data[1][yc * in->linesize[1]];
273         const uint16_t *pchromav = (uint16_t *)&in->data[2][yc * in->linesize[2]];
274
275         for (x = 0; x < w; x++) {
276             const int xc = x >> s->hsub;
277             const int luma    = pluma[x];
278             const int chromau = pchromau[xc];
279             const int chromav = pchromav[xc];
280             const int filt = luma    < 16 * mult || luma    > 235 * mult ||
281                 chromau < 16 * mult || chromau > 240 * mult ||
282                 chromav < 16 * mult || chromav > 240 * mult;
283             score += filt;
284             if (out && filt)
285                 burn_frame16(s, out, x, y);
286         }
287     }
288     return score;
289 }
290
291 static int filter_tout_outlier(uint8_t x, uint8_t y, uint8_t z)
292 {
293     return ((abs(x - y) + abs (z - y)) / 2) - abs(z - x) > 4; // make 4 configurable?
294 }
295
296 static int filter8_tout(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
297 {
298     ThreadData *td = arg;
299     const SignalstatsContext *s = ctx->priv;
300     const AVFrame *in = td->in;
301     AVFrame *out = td->out;
302     const int w = in->width;
303     const int h = in->height;
304     const int slice_start = (h *  jobnr   ) / nb_jobs;
305     const int slice_end   = (h * (jobnr+1)) / nb_jobs;
306     const uint8_t *p = in->data[0];
307     int lw = in->linesize[0];
308     int x, y, score = 0, filt;
309
310     for (y = slice_start; y < slice_end; y++) {
311
312         if (y - 1 < 0 || y + 1 >= h)
313             continue;
314
315         // detect two pixels above and below (to eliminate interlace artefacts)
316         // should check that video format is infact interlaced.
317
318 #define FILTER(i, j) \
319         filter_tout_outlier(p[(y-j) * lw + x + i], \
320                             p[    y * lw + x + i], \
321                             p[(y+j) * lw + x + i])
322
323 #define FILTER3(j) (FILTER(-1, j) && FILTER(0, j) && FILTER(1, j))
324
325         if (y - 2 >= 0 && y + 2 < h) {
326             for (x = 1; x < w - 1; x++) {
327                 filt = FILTER3(2) && FILTER3(1);
328                 score += filt;
329                 if (filt && out)
330                     burn_frame8(s, out, x, y);
331             }
332         } else {
333             for (x = 1; x < w - 1; x++) {
334                 filt = FILTER3(1);
335                 score += filt;
336                 if (filt && out)
337                     burn_frame8(s, out, x, y);
338             }
339         }
340     }
341     return score;
342 }
343
344 static int filter16_tout(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
345 {
346     ThreadData *td = arg;
347     const SignalstatsContext *s = ctx->priv;
348     const AVFrame *in = td->in;
349     AVFrame *out = td->out;
350     const int w = in->width;
351     const int h = in->height;
352     const int slice_start = (h *  jobnr   ) / nb_jobs;
353     const int slice_end   = (h * (jobnr+1)) / nb_jobs;
354     const uint16_t *p = (uint16_t *)in->data[0];
355     int lw = in->linesize[0] / 2;
356     int x, y, score = 0, filt;
357
358     for (y = slice_start; y < slice_end; y++) {
359
360         if (y - 1 < 0 || y + 1 >= h)
361             continue;
362
363         // detect two pixels above and below (to eliminate interlace artefacts)
364         // should check that video format is infact interlaced.
365
366         if (y - 2 >= 0 && y + 2 < h) {
367             for (x = 1; x < w - 1; x++) {
368                 filt = FILTER3(2) && FILTER3(1);
369                 score += filt;
370                 if (filt && out)
371                     burn_frame16(s, out, x, y);
372             }
373         } else {
374             for (x = 1; x < w - 1; x++) {
375                 filt = FILTER3(1);
376                 score += filt;
377                 if (filt && out)
378                     burn_frame16(s, out, x, y);
379             }
380         }
381     }
382     return score;
383 }
384
385 #define VREP_START 4
386
387 static int filter8_vrep(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
388 {
389     ThreadData *td = arg;
390     const SignalstatsContext *s = ctx->priv;
391     const AVFrame *in = td->in;
392     AVFrame *out = td->out;
393     const int w = in->width;
394     const int h = in->height;
395     const int slice_start = (h *  jobnr   ) / nb_jobs;
396     const int slice_end   = (h * (jobnr+1)) / nb_jobs;
397     const uint8_t *p = in->data[0];
398     const int lw = in->linesize[0];
399     int x, y, score = 0;
400
401     for (y = slice_start; y < slice_end; y++) {
402         const int y2lw = (y - VREP_START) * lw;
403         const int ylw  =  y               * lw;
404         int filt, totdiff = 0;
405
406         if (y < VREP_START)
407             continue;
408
409         for (x = 0; x < w; x++)
410             totdiff += abs(p[y2lw + x] - p[ylw + x]);
411         filt = totdiff < w;
412
413         score += filt;
414         if (filt && out)
415             for (x = 0; x < w; x++)
416                 burn_frame8(s, out, x, y);
417     }
418     return score * w;
419 }
420
421 static int filter16_vrep(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
422 {
423     ThreadData *td = arg;
424     const SignalstatsContext *s = ctx->priv;
425     const AVFrame *in = td->in;
426     AVFrame *out = td->out;
427     const int w = in->width;
428     const int h = in->height;
429     const int slice_start = (h *  jobnr   ) / nb_jobs;
430     const int slice_end   = (h * (jobnr+1)) / nb_jobs;
431     const uint16_t *p = (uint16_t *)in->data[0];
432     const int lw = in->linesize[0] / 2;
433     int x, y, score = 0;
434
435     for (y = slice_start; y < slice_end; y++) {
436         const int y2lw = (y - VREP_START) * lw;
437         const int ylw  =  y               * lw;
438         int64_t totdiff = 0;
439         int filt;
440
441         if (y < VREP_START)
442             continue;
443
444         for (x = 0; x < w; x++)
445             totdiff += abs(p[y2lw + x] - p[ylw + x]);
446         filt = totdiff < w;
447
448         score += filt;
449         if (filt && out)
450             for (x = 0; x < w; x++)
451                 burn_frame16(s, out, x, y);
452     }
453     return score * w;
454 }
455
456 static const struct {
457     const char *name;
458     int (*process8)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
459     int (*process16)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
460 } filters_def[] = {
461     {"TOUT", filter8_tout, filter16_tout},
462     {"VREP", filter8_vrep, filter16_vrep},
463     {"BRNG", filter8_brng, filter16_brng},
464     {NULL}
465 };
466
467 #define DEPTH 256
468
469 static int compute_sat_hue_metrics8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
470 {
471     int i, j;
472     ThreadDataHueSatMetrics *td = arg;
473     const SignalstatsContext *s = ctx->priv;
474     const AVFrame *src = td->src;
475     AVFrame *dst_sat = td->dst_sat;
476     AVFrame *dst_hue = td->dst_hue;
477
478     const int slice_start = (s->chromah *  jobnr   ) / nb_jobs;
479     const int slice_end   = (s->chromah * (jobnr+1)) / nb_jobs;
480
481     const int lsz_u = src->linesize[1];
482     const int lsz_v = src->linesize[2];
483     const uint8_t *p_u = src->data[1] + slice_start * lsz_u;
484     const uint8_t *p_v = src->data[2] + slice_start * lsz_v;
485
486     const int lsz_sat = dst_sat->linesize[0];
487     const int lsz_hue = dst_hue->linesize[0];
488     uint8_t *p_sat = dst_sat->data[0] + slice_start * lsz_sat;
489     uint8_t *p_hue = dst_hue->data[0] + slice_start * lsz_hue;
490
491     for (j = slice_start; j < slice_end; j++) {
492         for (i = 0; i < s->chromaw; i++) {
493             const int yuvu = p_u[i];
494             const int yuvv = p_v[i];
495             p_sat[i] = hypot(yuvu - 128, yuvv - 128); // int or round?
496             ((int16_t*)p_hue)[i] = fmod(floor((180 / M_PI) * atan2f(yuvu-128, yuvv-128) + 180), 360.);
497         }
498         p_u   += lsz_u;
499         p_v   += lsz_v;
500         p_sat += lsz_sat;
501         p_hue += lsz_hue;
502     }
503
504     return 0;
505 }
506
507 static int compute_sat_hue_metrics16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
508 {
509     int i, j;
510     ThreadDataHueSatMetrics *td = arg;
511     const SignalstatsContext *s = ctx->priv;
512     const AVFrame *src = td->src;
513     AVFrame *dst_sat = td->dst_sat;
514     AVFrame *dst_hue = td->dst_hue;
515     const int mid = 1 << (s->depth - 1);
516
517     const int slice_start = (s->chromah *  jobnr   ) / nb_jobs;
518     const int slice_end   = (s->chromah * (jobnr+1)) / nb_jobs;
519
520     const int lsz_u = src->linesize[1] / 2;
521     const int lsz_v = src->linesize[2] / 2;
522     const uint16_t *p_u = (uint16_t*)src->data[1] + slice_start * lsz_u;
523     const uint16_t *p_v = (uint16_t*)src->data[2] + slice_start * lsz_v;
524
525     const int lsz_sat = dst_sat->linesize[0] / 2;
526     const int lsz_hue = dst_hue->linesize[0] / 2;
527     uint16_t *p_sat = (uint16_t*)dst_sat->data[0] + slice_start * lsz_sat;
528     uint16_t *p_hue = (uint16_t*)dst_hue->data[0] + slice_start * lsz_hue;
529
530     for (j = slice_start; j < slice_end; j++) {
531         for (i = 0; i < s->chromaw; i++) {
532             const int yuvu = p_u[i];
533             const int yuvv = p_v[i];
534             p_sat[i] = hypot(yuvu - mid, yuvv - mid); // int or round?
535             ((int16_t*)p_hue)[i] = fmod(floor((180 / M_PI) * atan2f(yuvu-mid, yuvv-mid) + 180), 360.);
536         }
537         p_u   += lsz_u;
538         p_v   += lsz_v;
539         p_sat += lsz_sat;
540         p_hue += lsz_hue;
541     }
542
543     return 0;
544 }
545
546 static unsigned compute_bit_depth(uint16_t mask)
547 {
548     return av_popcount(mask);
549 }
550
551 static int filter_frame8(AVFilterLink *link, AVFrame *in)
552 {
553     AVFilterContext *ctx = link->dst;
554     SignalstatsContext *s = ctx->priv;
555     AVFilterLink *outlink = ctx->outputs[0];
556     AVFrame *out = in;
557     int i, j;
558     int  w = 0,  cw = 0, // in
559         pw = 0, cpw = 0; // prev
560     int fil;
561     char metabuf[128];
562     unsigned int histy[DEPTH] = {0},
563                  histu[DEPTH] = {0},
564                  histv[DEPTH] = {0},
565                  histhue[360] = {0},
566                  histsat[DEPTH] = {0}; // limited to 8 bit data.
567     int miny  = -1, minu  = -1, minv  = -1;
568     int maxy  = -1, maxu  = -1, maxv  = -1;
569     int lowy  = -1, lowu  = -1, lowv  = -1;
570     int highy = -1, highu = -1, highv = -1;
571     int minsat = -1, maxsat = -1, lowsat = -1, highsat = -1;
572     int lowp, highp, clowp, chighp;
573     int accy, accu, accv;
574     int accsat, acchue = 0;
575     int medhue, maxhue;
576     int toty = 0, totu = 0, totv = 0, totsat=0;
577     int tothue = 0;
578     int dify = 0, difu = 0, difv = 0;
579     uint16_t masky = 0, masku = 0, maskv = 0;
580
581     int filtot[FILT_NUMB] = {0};
582     AVFrame *prev;
583
584     AVFrame *sat = s->frame_sat;
585     AVFrame *hue = s->frame_hue;
586     const uint8_t *p_sat = sat->data[0];
587     const uint8_t *p_hue = hue->data[0];
588     const int lsz_sat = sat->linesize[0];
589     const int lsz_hue = hue->linesize[0];
590     ThreadDataHueSatMetrics td_huesat = {
591         .src     = in,
592         .dst_sat = sat,
593         .dst_hue = hue,
594     };
595
596     if (!s->frame_prev)
597         s->frame_prev = av_frame_clone(in);
598
599     prev = s->frame_prev;
600
601     if (s->outfilter != FILTER_NONE) {
602         out = av_frame_clone(in);
603         av_frame_make_writable(out);
604     }
605
606     ctx->internal->execute(ctx, compute_sat_hue_metrics8, &td_huesat,
607                            NULL, FFMIN(s->chromah, ff_filter_get_nb_threads(ctx)));
608
609     // Calculate luma histogram and difference with previous frame or field.
610     for (j = 0; j < link->h; j++) {
611         for (i = 0; i < link->w; i++) {
612             const int yuv = in->data[0][w + i];
613
614             masky |= yuv;
615             histy[yuv]++;
616             dify += abs(yuv - prev->data[0][pw + i]);
617         }
618         w  += in->linesize[0];
619         pw += prev->linesize[0];
620     }
621
622     // Calculate chroma histogram and difference with previous frame or field.
623     for (j = 0; j < s->chromah; j++) {
624         for (i = 0; i < s->chromaw; i++) {
625             const int yuvu = in->data[1][cw+i];
626             const int yuvv = in->data[2][cw+i];
627
628             masku |= yuvu;
629             maskv |= yuvv;
630             histu[yuvu]++;
631             difu += abs(yuvu - prev->data[1][cpw+i]);
632             histv[yuvv]++;
633             difv += abs(yuvv - prev->data[2][cpw+i]);
634
635             histsat[p_sat[i]]++;
636             histhue[((int16_t*)p_hue)[i]]++;
637         }
638         cw  += in->linesize[1];
639         cpw += prev->linesize[1];
640         p_sat += lsz_sat;
641         p_hue += lsz_hue;
642     }
643
644     for (fil = 0; fil < FILT_NUMB; fil ++) {
645         if (s->filters & 1<<fil) {
646             ThreadData td = {
647                 .in = in,
648                 .out = out != in && s->outfilter == fil ? out : NULL,
649             };
650             memset(s->jobs_rets, 0, s->nb_jobs * sizeof(*s->jobs_rets));
651             ctx->internal->execute(ctx, filters_def[fil].process8,
652                                    &td, s->jobs_rets, s->nb_jobs);
653             for (i = 0; i < s->nb_jobs; i++)
654                 filtot[fil] += s->jobs_rets[i];
655         }
656     }
657
658     // find low / high based on histogram percentile
659     // these only need to be calculated once.
660
661     lowp   = lrint(s->fs  * 10 / 100.);
662     highp  = lrint(s->fs  * 90 / 100.);
663     clowp  = lrint(s->cfs * 10 / 100.);
664     chighp = lrint(s->cfs * 90 / 100.);
665
666     accy = accu = accv = accsat = 0;
667     for (fil = 0; fil < DEPTH; fil++) {
668         if (miny   < 0 && histy[fil])   miny = fil;
669         if (minu   < 0 && histu[fil])   minu = fil;
670         if (minv   < 0 && histv[fil])   minv = fil;
671         if (minsat < 0 && histsat[fil]) minsat = fil;
672
673         if (histy[fil])   maxy   = fil;
674         if (histu[fil])   maxu   = fil;
675         if (histv[fil])   maxv   = fil;
676         if (histsat[fil]) maxsat = fil;
677
678         toty   += histy[fil]   * fil;
679         totu   += histu[fil]   * fil;
680         totv   += histv[fil]   * fil;
681         totsat += histsat[fil] * fil;
682
683         accy   += histy[fil];
684         accu   += histu[fil];
685         accv   += histv[fil];
686         accsat += histsat[fil];
687
688         if (lowy   == -1 && accy   >=  lowp) lowy   = fil;
689         if (lowu   == -1 && accu   >= clowp) lowu   = fil;
690         if (lowv   == -1 && accv   >= clowp) lowv   = fil;
691         if (lowsat == -1 && accsat >= clowp) lowsat = fil;
692
693         if (highy   == -1 && accy   >=  highp) highy   = fil;
694         if (highu   == -1 && accu   >= chighp) highu   = fil;
695         if (highv   == -1 && accv   >= chighp) highv   = fil;
696         if (highsat == -1 && accsat >= chighp) highsat = fil;
697     }
698
699     maxhue = histhue[0];
700     medhue = -1;
701     for (fil = 0; fil < 360; fil++) {
702         tothue += histhue[fil] * fil;
703         acchue += histhue[fil];
704
705         if (medhue == -1 && acchue > s->cfs / 2)
706             medhue = fil;
707         if (histhue[fil] > maxhue) {
708             maxhue = histhue[fil];
709         }
710     }
711
712     av_frame_free(&s->frame_prev);
713     s->frame_prev = av_frame_clone(in);
714
715 #define SET_META(key, fmt, val) do {                                \
716     snprintf(metabuf, sizeof(metabuf), fmt, val);                   \
717     av_dict_set(&out->metadata, "lavfi.signalstats." key, metabuf, 0);   \
718 } while (0)
719
720     SET_META("YMIN",    "%d", miny);
721     SET_META("YLOW",    "%d", lowy);
722     SET_META("YAVG",    "%g", 1.0 * toty / s->fs);
723     SET_META("YHIGH",   "%d", highy);
724     SET_META("YMAX",    "%d", maxy);
725
726     SET_META("UMIN",    "%d", minu);
727     SET_META("ULOW",    "%d", lowu);
728     SET_META("UAVG",    "%g", 1.0 * totu / s->cfs);
729     SET_META("UHIGH",   "%d", highu);
730     SET_META("UMAX",    "%d", maxu);
731
732     SET_META("VMIN",    "%d", minv);
733     SET_META("VLOW",    "%d", lowv);
734     SET_META("VAVG",    "%g", 1.0 * totv / s->cfs);
735     SET_META("VHIGH",   "%d", highv);
736     SET_META("VMAX",    "%d", maxv);
737
738     SET_META("SATMIN",  "%d", minsat);
739     SET_META("SATLOW",  "%d", lowsat);
740     SET_META("SATAVG",  "%g", 1.0 * totsat / s->cfs);
741     SET_META("SATHIGH", "%d", highsat);
742     SET_META("SATMAX",  "%d", maxsat);
743
744     SET_META("HUEMED",  "%d", medhue);
745     SET_META("HUEAVG",  "%g", 1.0 * tothue / s->cfs);
746
747     SET_META("YDIF",    "%g", 1.0 * dify / s->fs);
748     SET_META("UDIF",    "%g", 1.0 * difu / s->cfs);
749     SET_META("VDIF",    "%g", 1.0 * difv / s->cfs);
750
751     SET_META("YBITDEPTH", "%d", compute_bit_depth(masky));
752     SET_META("UBITDEPTH", "%d", compute_bit_depth(masku));
753     SET_META("VBITDEPTH", "%d", compute_bit_depth(maskv));
754
755     for (fil = 0; fil < FILT_NUMB; fil ++) {
756         if (s->filters & 1<<fil) {
757             char metaname[128];
758             snprintf(metabuf,  sizeof(metabuf),  "%g", 1.0 * filtot[fil] / s->fs);
759             snprintf(metaname, sizeof(metaname), "lavfi.signalstats.%s", filters_def[fil].name);
760             av_dict_set(&out->metadata, metaname, metabuf, 0);
761         }
762     }
763
764     if (in != out)
765         av_frame_free(&in);
766     return ff_filter_frame(outlink, out);
767 }
768
769 static int filter_frame16(AVFilterLink *link, AVFrame *in)
770 {
771     AVFilterContext *ctx = link->dst;
772     SignalstatsContext *s = ctx->priv;
773     AVFilterLink *outlink = ctx->outputs[0];
774     AVFrame *out = in;
775     int i, j;
776     int  w = 0,  cw = 0, // in
777         pw = 0, cpw = 0; // prev
778     int fil;
779     char metabuf[128];
780     unsigned int *histy = s->histy,
781                  *histu = s->histu,
782                  *histv = s->histv,
783                  histhue[360] = {0},
784                  *histsat = s->histsat;
785     int miny  = -1, minu  = -1, minv  = -1;
786     int maxy  = -1, maxu  = -1, maxv  = -1;
787     int lowy  = -1, lowu  = -1, lowv  = -1;
788     int highy = -1, highu = -1, highv = -1;
789     int minsat = -1, maxsat = -1, lowsat = -1, highsat = -1;
790     int lowp, highp, clowp, chighp;
791     int accy, accu, accv;
792     int accsat, acchue = 0;
793     int medhue, maxhue;
794     int64_t toty = 0, totu = 0, totv = 0, totsat=0;
795     int64_t tothue = 0;
796     int64_t dify = 0, difu = 0, difv = 0;
797     uint16_t masky = 0, masku = 0, maskv = 0;
798
799     int filtot[FILT_NUMB] = {0};
800     AVFrame *prev;
801
802     AVFrame *sat = s->frame_sat;
803     AVFrame *hue = s->frame_hue;
804     const uint16_t *p_sat = (uint16_t *)sat->data[0];
805     const uint16_t *p_hue = (uint16_t *)hue->data[0];
806     const int lsz_sat = sat->linesize[0] / 2;
807     const int lsz_hue = hue->linesize[0] / 2;
808     ThreadDataHueSatMetrics td_huesat = {
809         .src     = in,
810         .dst_sat = sat,
811         .dst_hue = hue,
812     };
813
814     if (!s->frame_prev)
815         s->frame_prev = av_frame_clone(in);
816
817     prev = s->frame_prev;
818
819     if (s->outfilter != FILTER_NONE) {
820         out = av_frame_clone(in);
821         av_frame_make_writable(out);
822     }
823
824     ctx->internal->execute(ctx, compute_sat_hue_metrics16, &td_huesat,
825                            NULL, FFMIN(s->chromah, ff_filter_get_nb_threads(ctx)));
826
827     // Calculate luma histogram and difference with previous frame or field.
828     memset(s->histy, 0, s->maxsize * sizeof(*s->histy));
829     for (j = 0; j < link->h; j++) {
830         for (i = 0; i < link->w; i++) {
831             const int yuv = AV_RN16(in->data[0] + w + i * 2);
832
833             masky |= yuv;
834             histy[yuv]++;
835             dify += abs(yuv - (int)AV_RN16(prev->data[0] + pw + i * 2));
836         }
837         w  += in->linesize[0];
838         pw += prev->linesize[0];
839     }
840
841     // Calculate chroma histogram and difference with previous frame or field.
842     memset(s->histu, 0, s->maxsize * sizeof(*s->histu));
843     memset(s->histv, 0, s->maxsize * sizeof(*s->histv));
844     memset(s->histsat, 0, s->maxsize * sizeof(*s->histsat));
845     for (j = 0; j < s->chromah; j++) {
846         for (i = 0; i < s->chromaw; i++) {
847             const int yuvu = AV_RN16(in->data[1] + cw + i * 2);
848             const int yuvv = AV_RN16(in->data[2] + cw + i * 2);
849
850             masku |= yuvu;
851             maskv |= yuvv;
852             histu[yuvu]++;
853             difu += abs(yuvu - (int)AV_RN16(prev->data[1] + cpw + i * 2));
854             histv[yuvv]++;
855             difv += abs(yuvv - (int)AV_RN16(prev->data[2] + cpw + i * 2));
856
857             histsat[p_sat[i]]++;
858             histhue[((int16_t*)p_hue)[i]]++;
859         }
860         cw  += in->linesize[1];
861         cpw += prev->linesize[1];
862         p_sat += lsz_sat;
863         p_hue += lsz_hue;
864     }
865
866     for (fil = 0; fil < FILT_NUMB; fil ++) {
867         if (s->filters & 1<<fil) {
868             ThreadData td = {
869                 .in = in,
870                 .out = out != in && s->outfilter == fil ? out : NULL,
871             };
872             memset(s->jobs_rets, 0, s->nb_jobs * sizeof(*s->jobs_rets));
873             ctx->internal->execute(ctx, filters_def[fil].process16,
874                                    &td, s->jobs_rets, s->nb_jobs);
875             for (i = 0; i < s->nb_jobs; i++)
876                 filtot[fil] += s->jobs_rets[i];
877         }
878     }
879
880     // find low / high based on histogram percentile
881     // these only need to be calculated once.
882
883     lowp   = lrint(s->fs  * 10 / 100.);
884     highp  = lrint(s->fs  * 90 / 100.);
885     clowp  = lrint(s->cfs * 10 / 100.);
886     chighp = lrint(s->cfs * 90 / 100.);
887
888     accy = accu = accv = accsat = 0;
889     for (fil = 0; fil < s->maxsize; fil++) {
890         if (miny   < 0 && histy[fil])   miny = fil;
891         if (minu   < 0 && histu[fil])   minu = fil;
892         if (minv   < 0 && histv[fil])   minv = fil;
893         if (minsat < 0 && histsat[fil]) minsat = fil;
894
895         if (histy[fil])   maxy   = fil;
896         if (histu[fil])   maxu   = fil;
897         if (histv[fil])   maxv   = fil;
898         if (histsat[fil]) maxsat = fil;
899
900         toty   += histy[fil]   * fil;
901         totu   += histu[fil]   * fil;
902         totv   += histv[fil]   * fil;
903         totsat += histsat[fil] * fil;
904
905         accy   += histy[fil];
906         accu   += histu[fil];
907         accv   += histv[fil];
908         accsat += histsat[fil];
909
910         if (lowy   == -1 && accy   >=  lowp) lowy   = fil;
911         if (lowu   == -1 && accu   >= clowp) lowu   = fil;
912         if (lowv   == -1 && accv   >= clowp) lowv   = fil;
913         if (lowsat == -1 && accsat >= clowp) lowsat = fil;
914
915         if (highy   == -1 && accy   >=  highp) highy   = fil;
916         if (highu   == -1 && accu   >= chighp) highu   = fil;
917         if (highv   == -1 && accv   >= chighp) highv   = fil;
918         if (highsat == -1 && accsat >= chighp) highsat = fil;
919     }
920
921     maxhue = histhue[0];
922     medhue = -1;
923     for (fil = 0; fil < 360; fil++) {
924         tothue += histhue[fil] * fil;
925         acchue += histhue[fil];
926
927         if (medhue == -1 && acchue > s->cfs / 2)
928             medhue = fil;
929         if (histhue[fil] > maxhue) {
930             maxhue = histhue[fil];
931         }
932     }
933
934     av_frame_free(&s->frame_prev);
935     s->frame_prev = av_frame_clone(in);
936
937     SET_META("YMIN",    "%d", miny);
938     SET_META("YLOW",    "%d", lowy);
939     SET_META("YAVG",    "%g", 1.0 * toty / s->fs);
940     SET_META("YHIGH",   "%d", highy);
941     SET_META("YMAX",    "%d", maxy);
942
943     SET_META("UMIN",    "%d", minu);
944     SET_META("ULOW",    "%d", lowu);
945     SET_META("UAVG",    "%g", 1.0 * totu / s->cfs);
946     SET_META("UHIGH",   "%d", highu);
947     SET_META("UMAX",    "%d", maxu);
948
949     SET_META("VMIN",    "%d", minv);
950     SET_META("VLOW",    "%d", lowv);
951     SET_META("VAVG",    "%g", 1.0 * totv / s->cfs);
952     SET_META("VHIGH",   "%d", highv);
953     SET_META("VMAX",    "%d", maxv);
954
955     SET_META("SATMIN",  "%d", minsat);
956     SET_META("SATLOW",  "%d", lowsat);
957     SET_META("SATAVG",  "%g", 1.0 * totsat / s->cfs);
958     SET_META("SATHIGH", "%d", highsat);
959     SET_META("SATMAX",  "%d", maxsat);
960
961     SET_META("HUEMED",  "%d", medhue);
962     SET_META("HUEAVG",  "%g", 1.0 * tothue / s->cfs);
963
964     SET_META("YDIF",    "%g", 1.0 * dify / s->fs);
965     SET_META("UDIF",    "%g", 1.0 * difu / s->cfs);
966     SET_META("VDIF",    "%g", 1.0 * difv / s->cfs);
967
968     SET_META("YBITDEPTH", "%d", compute_bit_depth(masky));
969     SET_META("UBITDEPTH", "%d", compute_bit_depth(masku));
970     SET_META("VBITDEPTH", "%d", compute_bit_depth(maskv));
971
972     for (fil = 0; fil < FILT_NUMB; fil ++) {
973         if (s->filters & 1<<fil) {
974             char metaname[128];
975             snprintf(metabuf,  sizeof(metabuf),  "%g", 1.0 * filtot[fil] / s->fs);
976             snprintf(metaname, sizeof(metaname), "lavfi.signalstats.%s", filters_def[fil].name);
977             av_dict_set(&out->metadata, metaname, metabuf, 0);
978         }
979     }
980
981     if (in != out)
982         av_frame_free(&in);
983     return ff_filter_frame(outlink, out);
984 }
985
986 static int filter_frame(AVFilterLink *link, AVFrame *in)
987 {
988     AVFilterContext *ctx = link->dst;
989     SignalstatsContext *s = ctx->priv;
990
991     if (s->depth > 8)
992         return filter_frame16(link, in);
993     else
994         return filter_frame8(link, in);
995 }
996
997 static const AVFilterPad signalstats_inputs[] = {
998     {
999         .name           = "default",
1000         .type           = AVMEDIA_TYPE_VIDEO,
1001         .filter_frame   = filter_frame,
1002     },
1003     { NULL }
1004 };
1005
1006 static const AVFilterPad signalstats_outputs[] = {
1007     {
1008         .name           = "default",
1009         .config_props   = config_output,
1010         .type           = AVMEDIA_TYPE_VIDEO,
1011     },
1012     { NULL }
1013 };
1014
1015 AVFilter ff_vf_signalstats = {
1016     .name          = "signalstats",
1017     .description   = "Generate statistics from video analysis.",
1018     .init          = init,
1019     .uninit        = uninit,
1020     .query_formats = query_formats,
1021     .priv_size     = sizeof(SignalstatsContext),
1022     .inputs        = signalstats_inputs,
1023     .outputs       = signalstats_outputs,
1024     .priv_class    = &signalstats_class,
1025     .flags         = AVFILTER_FLAG_SLICE_THREADS,
1026 };