FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/vf_signalstats.c
Date: 2024-04-19 17:50:32
Exec Total Coverage
Lines: 397 620 64.0%
Functions: 10 19 52.6%
Branches: 190 382 49.7%

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