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