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 | 2 | static int filter_frame(AVFilterLink *link, AVFrame *in) | |
542 | { | ||
543 | 2 | AVFilterContext *ctx = link->dst; | |
544 | 2 | SignalstatsContext *s = ctx->priv; | |
545 | 2 | AVFilterLink *outlink = ctx->outputs[0]; | |
546 | 2 | AVFrame *out = in; | |
547 | 2 | int w = 0, cw = 0, // in | |
548 | 2 | pw = 0, cpw = 0; // prev | |
549 | int fil; | ||
550 | char metabuf[128]; | ||
551 | 2 | unsigned int *histy = s->histy, | |
552 | 2 | *histu = s->histu, | |
553 | 2 | *histv = s->histv, | |
554 | 2 | histhue[360] = {0}, | |
555 | 2 | *histsat = s->histsat; | |
556 | 2 | int miny = -1, minu = -1, minv = -1; | |
557 | 2 | int maxy = -1, maxu = -1, maxv = -1; | |
558 | 2 | int lowy = -1, lowu = -1, lowv = -1; | |
559 | 2 | int highy = -1, highu = -1, highv = -1; | |
560 | 2 | int minsat = -1, maxsat = -1, lowsat = -1, highsat = -1; | |
561 | int lowp, highp, clowp, chighp; | ||
562 | int accy, accu, accv; | ||
563 | 2 | int accsat, acchue = 0; | |
564 | int medhue, maxhue; | ||
565 | 2 | int64_t toty = 0, totu = 0, totv = 0, totsat=0; | |
566 | 2 | int64_t tothue = 0; | |
567 | 2 | int64_t dify = 0, difu = 0, difv = 0; | |
568 | 2 | uint16_t masky = 0, masku = 0, maskv = 0; | |
569 | |||
570 | 2 | int filtot[FILT_NUMB] = {0}; | |
571 | AVFrame *prev; | ||
572 | int ret; | ||
573 | 2 | AVFrame *sat = s->frame_sat; | |
574 | 2 | AVFrame *hue = s->frame_hue; | |
575 | 2 | const int hbd = s->depth > 8; | |
576 | 2 | ThreadDataHueSatMetrics td_huesat = { | |
577 | .src = in, | ||
578 | .dst_sat = sat, | ||
579 | .dst_hue = hue, | ||
580 | }; | ||
581 | |||
582 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (!s->frame_prev) |
583 | 2 | s->frame_prev = av_frame_clone(in); | |
584 | |||
585 | 2 | prev = s->frame_prev; | |
586 | |||
587 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (s->outfilter != FILTER_NONE) { |
588 | ✗ | out = av_frame_clone(in); | |
589 | ✗ | if (!out) { | |
590 | ✗ | av_frame_free(&in); | |
591 | ✗ | return AVERROR(ENOMEM); | |
592 | } | ||
593 | ✗ | ret = ff_inlink_make_frame_writable(link, &out); | |
594 | ✗ | if (ret < 0) { | |
595 | ✗ | av_frame_free(&out); | |
596 | ✗ | av_frame_free(&in); | |
597 | ✗ | return ret; | |
598 | } | ||
599 | } | ||
600 | |||
601 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | ff_filter_execute(ctx, hbd ? compute_sat_hue_metrics16 |
602 | : compute_sat_hue_metrics8, &td_huesat, | ||
603 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | NULL, FFMIN(s->chromah, ff_filter_get_nb_threads(ctx))); |
604 | |||
605 | 2 | memset(s->histy, 0, s->maxsize * sizeof(*s->histy)); | |
606 | 2 | memset(s->histu, 0, s->maxsize * sizeof(*s->histu)); | |
607 | 2 | memset(s->histv, 0, s->maxsize * sizeof(*s->histv)); | |
608 | 2 | memset(s->histsat, 0, s->maxsize * sizeof(*s->histsat)); | |
609 | |||
610 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if (hbd) { |
611 | 1 | const uint16_t *p_sat = (uint16_t *)sat->data[0]; | |
612 | 1 | const uint16_t *p_hue = (uint16_t *)hue->data[0]; | |
613 | 1 | const int lsz_sat = sat->linesize[0] / 2; | |
614 | 1 | const int lsz_hue = hue->linesize[0] / 2; | |
615 | // Calculate luma histogram and difference with previous frame or field. | ||
616 |
2/2✓ Branch 0 taken 240 times.
✓ Branch 1 taken 1 times.
|
241 | for (int j = 0; j < link->h; j++) { |
617 |
2/2✓ Branch 0 taken 76800 times.
✓ Branch 1 taken 240 times.
|
77040 | for (int i = 0; i < link->w; i++) { |
618 | 76800 | const int yuv = AV_RN16(in->data[0] + w + i * 2); | |
619 | |||
620 | 76800 | masky |= yuv; | |
621 | 76800 | histy[yuv]++; | |
622 | 76800 | dify += abs(yuv - (int)AV_RN16(prev->data[0] + pw + i * 2)); | |
623 | } | ||
624 | 240 | w += in->linesize[0]; | |
625 | 240 | pw += prev->linesize[0]; | |
626 | } | ||
627 | |||
628 | // Calculate chroma histogram and difference with previous frame or field. | ||
629 |
2/2✓ Branch 0 taken 120 times.
✓ Branch 1 taken 1 times.
|
121 | for (int j = 0; j < s->chromah; j++) { |
630 |
2/2✓ Branch 0 taken 19200 times.
✓ Branch 1 taken 120 times.
|
19320 | for (int i = 0; i < s->chromaw; i++) { |
631 | 19200 | const int yuvu = AV_RN16(in->data[1] + cw + i * 2); | |
632 | 19200 | const int yuvv = AV_RN16(in->data[2] + cw + i * 2); | |
633 | |||
634 | 19200 | masku |= yuvu; | |
635 | 19200 | maskv |= yuvv; | |
636 | 19200 | histu[yuvu]++; | |
637 | 19200 | difu += abs(yuvu - (int)AV_RN16(prev->data[1] + cpw + i * 2)); | |
638 | 19200 | histv[yuvv]++; | |
639 | 19200 | difv += abs(yuvv - (int)AV_RN16(prev->data[2] + cpw + i * 2)); | |
640 | |||
641 | 19200 | histsat[p_sat[i]]++; | |
642 | 19200 | histhue[((int16_t*)p_hue)[i]]++; | |
643 | } | ||
644 | 120 | cw += in->linesize[1]; | |
645 | 120 | cpw += prev->linesize[1]; | |
646 | 120 | p_sat += lsz_sat; | |
647 | 120 | p_hue += lsz_hue; | |
648 | } | ||
649 | } else { | ||
650 | 1 | const uint8_t *p_sat = sat->data[0]; | |
651 | 1 | const uint8_t *p_hue = hue->data[0]; | |
652 | 1 | const int lsz_sat = sat->linesize[0]; | |
653 | 1 | const int lsz_hue = hue->linesize[0]; | |
654 | // Calculate luma histogram and difference with previous frame or field. | ||
655 |
2/2✓ Branch 0 taken 240 times.
✓ Branch 1 taken 1 times.
|
241 | for (int j = 0; j < link->h; j++) { |
656 |
2/2✓ Branch 0 taken 76800 times.
✓ Branch 1 taken 240 times.
|
77040 | for (int i = 0; i < link->w; i++) { |
657 | 76800 | const int yuv = in->data[0][w + i]; | |
658 | |||
659 | 76800 | masky |= yuv; | |
660 | 76800 | histy[yuv]++; | |
661 | 76800 | dify += abs(yuv - prev->data[0][pw + i]); | |
662 | } | ||
663 | 240 | w += in->linesize[0]; | |
664 | 240 | pw += prev->linesize[0]; | |
665 | } | ||
666 | |||
667 | // Calculate chroma histogram and difference with previous frame or field. | ||
668 |
2/2✓ Branch 0 taken 120 times.
✓ Branch 1 taken 1 times.
|
121 | for (int j = 0; j < s->chromah; j++) { |
669 |
2/2✓ Branch 0 taken 19200 times.
✓ Branch 1 taken 120 times.
|
19320 | for (int i = 0; i < s->chromaw; i++) { |
670 | 19200 | const int yuvu = in->data[1][cw+i]; | |
671 | 19200 | const int yuvv = in->data[2][cw+i]; | |
672 | |||
673 | 19200 | masku |= yuvu; | |
674 | 19200 | maskv |= yuvv; | |
675 | 19200 | histu[yuvu]++; | |
676 | 19200 | difu += abs(yuvu - prev->data[1][cpw+i]); | |
677 | 19200 | histv[yuvv]++; | |
678 | 19200 | difv += abs(yuvv - prev->data[2][cpw+i]); | |
679 | |||
680 | 19200 | histsat[p_sat[i]]++; | |
681 | 19200 | histhue[((int16_t*)p_hue)[i]]++; | |
682 | } | ||
683 | 120 | cw += in->linesize[1]; | |
684 | 120 | cpw += prev->linesize[1]; | |
685 | 120 | p_sat += lsz_sat; | |
686 | 120 | p_hue += lsz_hue; | |
687 | } | ||
688 | } | ||
689 | |||
690 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
|
8 | for (fil = 0; fil < FILT_NUMB; fil ++) { |
691 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (s->filters & 1<<fil) { |
692 | ✗ | ThreadData td = { | |
693 | .in = in, | ||
694 | ✗ | .out = out != in && s->outfilter == fil ? out : NULL, | |
695 | }; | ||
696 | ✗ | memset(s->jobs_rets, 0, s->nb_jobs * sizeof(*s->jobs_rets)); | |
697 | ✗ | ff_filter_execute(ctx, hbd ? filters_def[fil].process16 : filters_def[fil].process8, | |
698 | &td, s->jobs_rets, s->nb_jobs); | ||
699 | ✗ | for (int i = 0; i < s->nb_jobs; i++) | |
700 | ✗ | filtot[fil] += s->jobs_rets[i]; | |
701 | } | ||
702 | } | ||
703 | |||
704 | // find low / high based on histogram percentile | ||
705 | // these only need to be calculated once. | ||
706 | |||
707 | 2 | lowp = lrint(s->fs * 10 / 100.); | |
708 | 2 | highp = lrint(s->fs * 90 / 100.); | |
709 | 2 | clowp = lrint(s->cfs * 10 / 100.); | |
710 | 2 | chighp = lrint(s->cfs * 90 / 100.); | |
711 | |||
712 | 2 | accy = accu = accv = accsat = 0; | |
713 |
2/2✓ Branch 0 taken 1280 times.
✓ Branch 1 taken 2 times.
|
1282 | for (fil = 0; fil < s->maxsize; fil++) { |
714 |
4/4✓ Branch 0 taken 1180 times.
✓ Branch 1 taken 100 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1178 times.
|
1280 | if (miny < 0 && histy[fil]) miny = fil; |
715 |
4/4✓ Branch 0 taken 644 times.
✓ Branch 1 taken 636 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 642 times.
|
1280 | if (minu < 0 && histu[fil]) minu = fil; |
716 |
4/4✓ Branch 0 taken 644 times.
✓ Branch 1 taken 636 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 642 times.
|
1280 | if (minv < 0 && histv[fil]) minv = fil; |
717 |
4/4✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1276 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
|
1280 | if (minsat < 0 && histsat[fil]) minsat = fil; |
718 | |||
719 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1278 times.
|
1280 | if (histy[fil]) maxy = fil; |
720 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1278 times.
|
1280 | if (histu[fil]) maxu = fil; |
721 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1278 times.
|
1280 | if (histv[fil]) maxv = fil; |
722 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1278 times.
|
1280 | if (histsat[fil]) maxsat = fil; |
723 | |||
724 | 1280 | toty += (uint64_t)histy[fil] * fil; | |
725 | 1280 | totu += (uint64_t)histu[fil] * fil; | |
726 | 1280 | totv += (uint64_t)histv[fil] * fil; | |
727 | 1280 | totsat += (uint64_t)histsat[fil] * fil; | |
728 | |||
729 | 1280 | accy += histy[fil]; | |
730 | 1280 | accu += histu[fil]; | |
731 | 1280 | accv += histv[fil]; | |
732 | 1280 | accsat += histsat[fil]; | |
733 | |||
734 |
4/4✓ Branch 0 taken 1180 times.
✓ Branch 1 taken 100 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1178 times.
|
1280 | if (lowy == -1 && accy >= lowp) lowy = fil; |
735 |
4/4✓ Branch 0 taken 644 times.
✓ Branch 1 taken 636 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 642 times.
|
1280 | if (lowu == -1 && accu >= clowp) lowu = fil; |
736 |
4/4✓ Branch 0 taken 644 times.
✓ Branch 1 taken 636 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 642 times.
|
1280 | if (lowv == -1 && accv >= clowp) lowv = fil; |
737 |
4/4✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1276 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
|
1280 | if (lowsat == -1 && accsat >= clowp) lowsat = fil; |
738 | |||
739 |
4/4✓ Branch 0 taken 1180 times.
✓ Branch 1 taken 100 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1178 times.
|
1280 | if (highy == -1 && accy >= highp) highy = fil; |
740 |
4/4✓ Branch 0 taken 644 times.
✓ Branch 1 taken 636 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 642 times.
|
1280 | if (highu == -1 && accu >= chighp) highu = fil; |
741 |
4/4✓ Branch 0 taken 644 times.
✓ Branch 1 taken 636 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 642 times.
|
1280 | if (highv == -1 && accv >= chighp) highv = fil; |
742 |
4/4✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1276 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
|
1280 | if (highsat == -1 && accsat >= chighp) highsat = fil; |
743 | } | ||
744 | |||
745 | 2 | maxhue = histhue[0]; | |
746 | 2 | medhue = -1; | |
747 |
2/2✓ Branch 0 taken 720 times.
✓ Branch 1 taken 2 times.
|
722 | for (fil = 0; fil < 360; fil++) { |
748 | 720 | tothue += (uint64_t)histhue[fil] * fil; | |
749 | 720 | acchue += histhue[fil]; | |
750 | |||
751 |
4/4✓ Branch 0 taken 407 times.
✓ Branch 1 taken 313 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 405 times.
|
720 | if (medhue == -1 && acchue > s->cfs / 2) |
752 | 2 | medhue = fil; | |
753 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 718 times.
|
720 | if (histhue[fil] > maxhue) { |
754 | 2 | maxhue = histhue[fil]; | |
755 | } | ||
756 | } | ||
757 | |||
758 | 2 | av_frame_free(&s->frame_prev); | |
759 | 2 | s->frame_prev = av_frame_clone(in); | |
760 | |||
761 | #define SET_META(key, fmt, val) do { \ | ||
762 | snprintf(metabuf, sizeof(metabuf), fmt, val); \ | ||
763 | av_dict_set(&out->metadata, "lavfi.signalstats." key, metabuf, 0); \ | ||
764 | } while (0) | ||
765 | |||
766 | 2 | av_dict_set_int(&out->metadata, "lavfi.signalstats.YMIN", miny, 0); | |
767 | 2 | av_dict_set_int(&out->metadata, "lavfi.signalstats.YLOW", lowy, 0); | |
768 | 2 | SET_META("YAVG", "%g", 1.0 * toty / s->fs); | |
769 | 2 | av_dict_set_int(&out->metadata, "lavfi.signalstats.YHIGH", highy, 0); | |
770 | 2 | av_dict_set_int(&out->metadata, "lavfi.signalstats.YMAX", maxy, 0); | |
771 | |||
772 | 2 | av_dict_set_int(&out->metadata, "lavfi.signalstats.UMIN", minu, 0); | |
773 | 2 | av_dict_set_int(&out->metadata, "lavfi.signalstats.ULOW", lowu, 0); | |
774 | 2 | SET_META("UAVG", "%g", 1.0 * totu / s->cfs); | |
775 | 2 | av_dict_set_int(&out->metadata, "lavfi.signalstats.UHIGH", highu, 0); | |
776 | 2 | av_dict_set_int(&out->metadata, "lavfi.signalstats.UMAX", maxu, 0); | |
777 | |||
778 | 2 | av_dict_set_int(&out->metadata, "lavfi.signalstats.VMIN", minv, 0); | |
779 | 2 | av_dict_set_int(&out->metadata, "lavfi.signalstats.VLOW", lowv, 0); | |
780 | 2 | SET_META("VAVG", "%g", 1.0 * totv / s->cfs); | |
781 | 2 | av_dict_set_int(&out->metadata, "lavfi.signalstats.VHIGH", highv, 0); | |
782 | 2 | av_dict_set_int(&out->metadata, "lavfi.signalstats.VMAX", maxv, 0); | |
783 | |||
784 | 2 | av_dict_set_int(&out->metadata, "lavfi.signalstats.SATMIN", minsat, 0); | |
785 | 2 | av_dict_set_int(&out->metadata, "lavfi.signalstats.SATLOW", lowsat, 0); | |
786 | 2 | SET_META("SATAVG", "%g", 1.0 * totsat / s->cfs); | |
787 | 2 | av_dict_set_int(&out->metadata, "lavfi.signalstats.SATHIGH", highsat, 0); | |
788 | 2 | av_dict_set_int(&out->metadata, "lavfi.signalstats.SATMAX", maxsat, 0); | |
789 | |||
790 | 2 | av_dict_set_int(&out->metadata, "lavfi.signalstats.HUEMED", medhue, 0); | |
791 | 2 | SET_META("HUEAVG", "%g", 1.0 * tothue / s->cfs); | |
792 | |||
793 | 2 | SET_META("YDIF", "%g", 1.0 * dify / s->fs); | |
794 | 2 | SET_META("UDIF", "%g", 1.0 * difu / s->cfs); | |
795 | 2 | SET_META("VDIF", "%g", 1.0 * difv / s->cfs); | |
796 | |||
797 | 2 | av_dict_set_int(&out->metadata, "lavfi.signalstats.YBITDEPTH", compute_bit_depth(masky), 0); | |
798 | 2 | av_dict_set_int(&out->metadata, "lavfi.signalstats.UBITDEPTH", compute_bit_depth(masku), 0); | |
799 | 2 | av_dict_set_int(&out->metadata, "lavfi.signalstats.VBITDEPTH", compute_bit_depth(maskv), 0); | |
800 | |||
801 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
|
8 | for (fil = 0; fil < FILT_NUMB; fil ++) { |
802 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (s->filters & 1<<fil) { |
803 | char metaname[128]; | ||
804 | ✗ | snprintf(metabuf, sizeof(metabuf), "%g", 1.0 * filtot[fil] / s->fs); | |
805 | ✗ | snprintf(metaname, sizeof(metaname), "lavfi.signalstats.%s", filters_def[fil].name); | |
806 | ✗ | av_dict_set(&out->metadata, metaname, metabuf, 0); | |
807 | } | ||
808 | } | ||
809 | |||
810 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (in != out) |
811 | ✗ | av_frame_free(&in); | |
812 | 2 | return ff_filter_frame(outlink, out); | |
813 | } | ||
814 | |||
815 | static const AVFilterPad signalstats_inputs[] = { | ||
816 | { | ||
817 | .name = "default", | ||
818 | .type = AVMEDIA_TYPE_VIDEO, | ||
819 | .filter_frame = filter_frame, | ||
820 | }, | ||
821 | }; | ||
822 | |||
823 | static const AVFilterPad signalstats_outputs[] = { | ||
824 | { | ||
825 | .name = "default", | ||
826 | .config_props = config_output, | ||
827 | .type = AVMEDIA_TYPE_VIDEO, | ||
828 | }, | ||
829 | }; | ||
830 | |||
831 | const AVFilter ff_vf_signalstats = { | ||
832 | .name = "signalstats", | ||
833 | .description = "Generate statistics from video analysis.", | ||
834 | .init = init, | ||
835 | .uninit = uninit, | ||
836 | .priv_size = sizeof(SignalstatsContext), | ||
837 | FILTER_INPUTS(signalstats_inputs), | ||
838 | FILTER_OUTPUTS(signalstats_outputs), | ||
839 | FILTER_PIXFMTS_ARRAY(pix_fmts), | ||
840 | .priv_class = &signalstats_class, | ||
841 | .flags = AVFILTER_FLAG_SLICE_THREADS, | ||
842 | }; | ||
843 |