LCOV - code coverage report
Current view: top level - libavfilter - vf_signalstats.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 607 0.0 %
Date: 2017-12-13 10:57:33 Functions: 0 20 0.0 %

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

Generated by: LCOV version 1.13