FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/vf_histogram.c
Date: 2024-04-25 15:36:26
Exec Total Coverage
Lines: 136 310 43.9%
Functions: 4 5 80.0%
Branches: 83 244 34.0%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2012-2019 Paul B Mahol
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include "config_components.h"
22
23 #include "libavutil/colorspace.h"
24 #include "libavutil/opt.h"
25 #include "libavutil/parseutils.h"
26 #include "libavutil/pixdesc.h"
27 #include "libavutil/imgutils.h"
28 #include "libavutil/intreadwrite.h"
29 #include "avfilter.h"
30 #include "formats.h"
31 #include "internal.h"
32 #include "video.h"
33
34 typedef struct HistogramContext {
35 const AVClass *class; ///< AVClass context for log and options purpose
36 int thistogram;
37 int envelope;
38 int slide;
39 unsigned histogram[256*256];
40 int histogram_size;
41 int width;
42 int x_pos;
43 int mult;
44 int mid;
45 int ncomp;
46 int dncomp;
47 uint8_t bg_color[4][4];
48 uint8_t fg_color[4][4];
49 uint8_t envelope_rgba[4];
50 uint8_t envelope_color[4];
51 int level_height;
52 int scale_height;
53 int display_mode;
54 int colors_mode;
55 int levels_mode;
56 const AVPixFmtDescriptor *desc, *odesc;
57 int components;
58 float fgopacity;
59 float bgopacity;
60 int planewidth[4];
61 int planeheight[4];
62 int start[4];
63 AVFrame *out;
64 } HistogramContext;
65
66 #define OFFSET(x) offsetof(HistogramContext, x)
67 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
68
69 #define COMMON_OPTIONS \
70 { "display_mode", "set display mode", OFFSET(display_mode), AV_OPT_TYPE_INT, {.i64=2}, 0, 2, FLAGS, .unit = "display_mode"}, \
71 { "d", "set display mode", OFFSET(display_mode), AV_OPT_TYPE_INT, {.i64=2}, 0, 2, FLAGS, .unit = "display_mode"}, \
72 { "overlay", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "display_mode" }, \
73 { "parade", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "display_mode" }, \
74 { "stack", NULL, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, .unit = "display_mode" }, \
75 { "levels_mode", "set levels mode", OFFSET(levels_mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "levels_mode"}, \
76 { "m", "set levels mode", OFFSET(levels_mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "levels_mode"}, \
77 { "linear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "levels_mode" }, \
78 { "logarithmic", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "levels_mode" }, \
79 { "components", "set color components to display", OFFSET(components), AV_OPT_TYPE_INT, {.i64=7}, 1, 15, FLAGS}, \
80 { "c", "set color components to display", OFFSET(components), AV_OPT_TYPE_INT, {.i64=7}, 1, 15, FLAGS},
81
82 static const AVOption histogram_options[] = {
83 { "level_height", "set level height", OFFSET(level_height), AV_OPT_TYPE_INT, {.i64=200}, 50, 2048, FLAGS},
84 { "scale_height", "set scale height", OFFSET(scale_height), AV_OPT_TYPE_INT, {.i64=12}, 0, 40, FLAGS},
85 COMMON_OPTIONS
86 { "fgopacity", "set foreground opacity", OFFSET(fgopacity), AV_OPT_TYPE_FLOAT, {.dbl=0.7}, 0, 1, FLAGS},
87 { "f", "set foreground opacity", OFFSET(fgopacity), AV_OPT_TYPE_FLOAT, {.dbl=0.7}, 0, 1, FLAGS},
88 { "bgopacity", "set background opacity", OFFSET(bgopacity), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGS},
89 { "b", "set background opacity", OFFSET(bgopacity), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGS},
90 { "colors_mode", "set colors mode", OFFSET(colors_mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 9, FLAGS, .unit = "colors_mode"},
91 { "l", "set colors mode", OFFSET(colors_mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 9, FLAGS, .unit = "colors_mode"},
92 { "whiteonblack", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "colors_mode" },
93 { "blackonwhite", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "colors_mode" },
94 { "whiteongray", NULL, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, .unit = "colors_mode" },
95 { "blackongray", NULL, 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, FLAGS, .unit = "colors_mode" },
96 { "coloronblack", NULL, 0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, FLAGS, .unit = "colors_mode" },
97 { "coloronwhite", NULL, 0, AV_OPT_TYPE_CONST, {.i64=5}, 0, 0, FLAGS, .unit = "colors_mode" },
98 { "colorongray" , NULL, 0, AV_OPT_TYPE_CONST, {.i64=6}, 0, 0, FLAGS, .unit = "colors_mode" },
99 { "blackoncolor", NULL, 0, AV_OPT_TYPE_CONST, {.i64=7}, 0, 0, FLAGS, .unit = "colors_mode" },
100 { "whiteoncolor", NULL, 0, AV_OPT_TYPE_CONST, {.i64=8}, 0, 0, FLAGS, .unit = "colors_mode" },
101 { "grayoncolor" , NULL, 0, AV_OPT_TYPE_CONST, {.i64=9}, 0, 0, FLAGS, .unit = "colors_mode" },
102 { NULL }
103 };
104
105 AVFILTER_DEFINE_CLASS(histogram);
106
107 static const enum AVPixelFormat levels_in_pix_fmts[] = {
108 AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVJ420P,
109 AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVJ422P,
110 AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUVJ411P,
111 AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV410P,
112 AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P,
113 AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
114 AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9,
115 AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10,
116 AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10,
117 AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12,
118 AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12,
119 AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP,
120 AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10,
121 AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12,
122 AV_PIX_FMT_GRAY8,
123 AV_PIX_FMT_NONE
124 };
125
126 static const enum AVPixelFormat levels_out_yuv8_pix_fmts[] = {
127 AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P,
128 AV_PIX_FMT_NONE
129 };
130
131 static const enum AVPixelFormat levels_out_yuv9_pix_fmts[] = {
132 AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUV444P9,
133 AV_PIX_FMT_NONE
134 };
135
136 static const enum AVPixelFormat levels_out_yuv10_pix_fmts[] = {
137 AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUV444P10,
138 AV_PIX_FMT_NONE
139 };
140
141 static const enum AVPixelFormat levels_out_yuv12_pix_fmts[] = {
142 AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUV444P12,
143 AV_PIX_FMT_NONE
144 };
145
146 static const enum AVPixelFormat levels_out_rgb8_pix_fmts[] = {
147 AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP,
148 AV_PIX_FMT_NONE
149 };
150
151 static const enum AVPixelFormat levels_out_rgb9_pix_fmts[] = {
152 AV_PIX_FMT_GBRP9,
153 AV_PIX_FMT_NONE
154 };
155
156 static const enum AVPixelFormat levels_out_rgb10_pix_fmts[] = {
157 AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10,
158 AV_PIX_FMT_NONE
159 };
160
161 static const enum AVPixelFormat levels_out_rgb12_pix_fmts[] = {
162 AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12,
163 AV_PIX_FMT_NONE
164 };
165
166 2 static int query_formats(AVFilterContext *ctx)
167 {
168 AVFilterFormats *avff;
169 const AVPixFmtDescriptor *desc;
170 const enum AVPixelFormat *out_pix_fmts;
171 int rgb, i, bits;
172 int ret;
173
174
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (!ctx->inputs[0]->incfg.formats ||
175
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 !ctx->inputs[0]->incfg.formats->nb_formats) {
176 1 return AVERROR(EAGAIN);
177 }
178
179
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (!ctx->inputs[0]->outcfg.formats)
180
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if ((ret = ff_formats_ref(ff_make_format_list(levels_in_pix_fmts), &ctx->inputs[0]->outcfg.formats)) < 0)
181 return ret;
182 1 avff = ctx->inputs[0]->incfg.formats;
183 1 desc = av_pix_fmt_desc_get(avff->formats[0]);
184 1 rgb = desc->flags & AV_PIX_FMT_FLAG_RGB;
185 1 bits = desc->comp[0].depth;
186
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 for (i = 1; i < avff->nb_formats; i++) {
187 desc = av_pix_fmt_desc_get(avff->formats[i]);
188 if ((rgb != (desc->flags & AV_PIX_FMT_FLAG_RGB)) ||
189 (bits != desc->comp[0].depth))
190 return AVERROR(EAGAIN);
191 }
192
193
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 if (rgb && bits == 8)
194 out_pix_fmts = levels_out_rgb8_pix_fmts;
195
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 else if (rgb && bits == 9)
196 out_pix_fmts = levels_out_rgb9_pix_fmts;
197
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 else if (rgb && bits == 10)
198 out_pix_fmts = levels_out_rgb10_pix_fmts;
199
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 else if (rgb && bits == 12)
200 out_pix_fmts = levels_out_rgb12_pix_fmts;
201
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 else if (bits == 8)
202 1 out_pix_fmts = levels_out_yuv8_pix_fmts;
203 else if (bits == 9)
204 out_pix_fmts = levels_out_yuv9_pix_fmts;
205 else if (bits == 10)
206 out_pix_fmts = levels_out_yuv10_pix_fmts;
207 else if (bits == 12)
208 out_pix_fmts = levels_out_yuv12_pix_fmts;
209 else
210 return AVERROR(EAGAIN);
211
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if ((ret = ff_formats_ref(ff_make_format_list(out_pix_fmts), &ctx->outputs[0]->incfg.formats)) < 0)
212 return ret;
213
214 1 return 0;
215 }
216
217 static const uint8_t black_yuva_color[4] = { 0, 127, 127, 255 };
218 static const uint8_t black_gbrp_color[4] = { 0, 0, 0, 255 };
219 static const uint8_t white_yuva_color[4] = { 255, 127, 127, 255 };
220 static const uint8_t white_gbrp_color[4] = { 255, 255, 255, 255 };
221 static const uint8_t gray_color[4] = { 127, 127, 127, 255 };
222 static const uint8_t red_yuva_color[4] = { 127, 127, 255, 255 };
223 static const uint8_t red_gbrp_color[4] = { 255, 0, 0, 255 };
224 static const uint8_t green_yuva_color[4] = { 255, 127, 127, 255 };
225 static const uint8_t igreen_yuva_color[4]= { 0, 127, 127, 255 };
226 static const uint8_t green_gbrp_color[4] = { 0, 255, 0, 255 };
227 static const uint8_t blue_yuva_color[4] = { 127, 255, 127, 255 };
228 static const uint8_t blue_gbrp_color[4] = { 0, 0, 255, 255 };
229
230 1 static int config_input(AVFilterLink *inlink)
231 {
232 1 HistogramContext *s = inlink->dst->priv;
233 1 int rgb = 0;
234
235 1 s->desc = av_pix_fmt_desc_get(inlink->format);
236 1 s->ncomp = s->desc->nb_components;
237 1 s->histogram_size = 1 << s->desc->comp[0].depth;
238 1 s->mult = s->histogram_size / 256;
239
240
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 switch (inlink->format) {
241 case AV_PIX_FMT_GBRAP12:
242 case AV_PIX_FMT_GBRP12:
243 case AV_PIX_FMT_GBRAP10:
244 case AV_PIX_FMT_GBRP10:
245 case AV_PIX_FMT_GBRP9:
246 case AV_PIX_FMT_GBRAP:
247 case AV_PIX_FMT_GBRP:
248 memcpy(s->bg_color[0], black_gbrp_color, 4);
249 memcpy(s->fg_color[0], white_gbrp_color, 4);
250 s->start[0] = s->start[1] = s->start[2] = s->start[3] = 0;
251 memcpy(s->envelope_color, s->envelope_rgba, 4);
252 rgb = 1;
253 break;
254 1 default:
255 1 s->mid = 127;
256 1 memcpy(s->bg_color[0], black_yuva_color, 4);
257 1 memcpy(s->fg_color[0], white_yuva_color, 4);
258 1 s->start[0] = s->start[3] = 0;
259 1 s->start[1] = s->start[2] = s->histogram_size / 2;
260 1 s->envelope_color[0] = RGB_TO_Y_BT709(s->envelope_rgba[0], s->envelope_rgba[1], s->envelope_rgba[2]);
261 1 s->envelope_color[1] = RGB_TO_U_BT709(s->envelope_rgba[0], s->envelope_rgba[1], s->envelope_rgba[2], 0);
262 1 s->envelope_color[2] = RGB_TO_V_BT709(s->envelope_rgba[0], s->envelope_rgba[1], s->envelope_rgba[2], 0);
263 1 s->envelope_color[3] = s->envelope_rgba[3];
264 }
265
266
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 for (int i = 1; i < 4; i++) {
267 3 memcpy(s->fg_color[i], s->fg_color[0], 4);
268 3 memcpy(s->bg_color[i], s->bg_color[0], 4);
269 }
270
271
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (s->display_mode) {
272
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (s->colors_mode == 1) {
273 for (int i = 0; i < 4; i++)
274 for (int j = 0; j < 4; j++)
275 FFSWAP(uint8_t, s->fg_color[i][j], s->bg_color[i][j]);
276
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 } else if (s->colors_mode == 2) {
277 for (int i = 0; i < 4; i++)
278 memcpy(s->bg_color[i], gray_color, 4);
279
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 } else if (s->colors_mode == 3) {
280 for (int i = 0; i < 4; i++)
281 for (int j = 0; j < 4; j++)
282 FFSWAP(uint8_t, s->fg_color[i][j], s->bg_color[i][j]);
283 for (int i = 0; i < 4; i++)
284 memcpy(s->bg_color[i], gray_color, 4);
285
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 } else if (s->colors_mode == 4) {
286 if (rgb) {
287 memcpy(s->fg_color[0], red_gbrp_color, 4);
288 memcpy(s->fg_color[1], green_gbrp_color, 4);
289 memcpy(s->fg_color[2], blue_gbrp_color, 4);
290 } else {
291 memcpy(s->fg_color[0], green_yuva_color, 4);
292 memcpy(s->fg_color[1], blue_yuva_color, 4);
293 memcpy(s->fg_color[2], red_yuva_color, 4);
294 }
295
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 } else if (s->colors_mode == 5) {
296 for (int i = 0; i < 4; i++)
297 for (int j = 0; j < 4; j++)
298 FFSWAP(uint8_t, s->fg_color[i][j], s->bg_color[i][j]);
299 if (rgb) {
300 memcpy(s->fg_color[0], red_gbrp_color, 4);
301 memcpy(s->fg_color[1], green_gbrp_color, 4);
302 memcpy(s->fg_color[2], blue_gbrp_color, 4);
303 } else {
304 memcpy(s->fg_color[0], igreen_yuva_color,4);
305 memcpy(s->fg_color[1], blue_yuva_color, 4);
306 memcpy(s->fg_color[2], red_yuva_color, 4);
307 }
308
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 } else if (s->colors_mode == 6) {
309 for (int i = 0; i < 4; i++)
310 memcpy(s->bg_color[i], gray_color, 4);
311 if (rgb) {
312 memcpy(s->fg_color[0], red_gbrp_color, 4);
313 memcpy(s->fg_color[1], green_gbrp_color, 4);
314 memcpy(s->fg_color[2], blue_gbrp_color, 4);
315 } else {
316 memcpy(s->fg_color[0], green_yuva_color, 4);
317 memcpy(s->fg_color[1], blue_yuva_color, 4);
318 memcpy(s->fg_color[2], red_yuva_color, 4);
319 }
320
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 } else if (s->colors_mode == 7) {
321 for (int i = 0; i < 4; i++)
322 for (int j = 0; j < 4; j++)
323 FFSWAP(uint8_t, s->fg_color[i][j], s->bg_color[i][j]);
324 if (rgb) {
325 memcpy(s->bg_color[0], red_gbrp_color, 4);
326 memcpy(s->bg_color[1], green_gbrp_color, 4);
327 memcpy(s->bg_color[2], blue_gbrp_color, 4);
328 } else {
329 memcpy(s->bg_color[0], green_yuva_color, 4);
330 memcpy(s->bg_color[1], blue_yuva_color, 4);
331 memcpy(s->bg_color[2], red_yuva_color, 4);
332 }
333
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 } else if (s->colors_mode == 8) {
334 if (rgb) {
335 memcpy(s->bg_color[0], red_gbrp_color, 4);
336 memcpy(s->bg_color[1], green_gbrp_color, 4);
337 memcpy(s->bg_color[2], blue_gbrp_color, 4);
338 } else {
339 memcpy(s->bg_color[0], igreen_yuva_color,4);
340 memcpy(s->bg_color[1], blue_yuva_color, 4);
341 memcpy(s->bg_color[2], red_yuva_color, 4);
342 }
343
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 } else if (s->colors_mode == 9) {
344 for (int i = 0; i < 4; i++)
345 memcpy(s->fg_color[i], gray_color, 4);
346 if (rgb) {
347 memcpy(s->bg_color[0], red_gbrp_color, 4);
348 memcpy(s->bg_color[1], green_gbrp_color, 4);
349 memcpy(s->bg_color[2], blue_gbrp_color, 4);
350 } else {
351 memcpy(s->bg_color[0], igreen_yuva_color,4);
352 memcpy(s->bg_color[1], blue_yuva_color, 4);
353 memcpy(s->bg_color[2], red_yuva_color, 4);
354 }
355 }
356 }
357
358
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
5 for (int i = 0; i < 4; i++) {
359 4 s->fg_color[i][3] = s->fgopacity * 255;
360 4 s->bg_color[i][3] = s->bgopacity * 255;
361 }
362
363 1 s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, s->desc->log2_chroma_h);
364 1 s->planeheight[0] = s->planeheight[3] = inlink->h;
365 1 s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, s->desc->log2_chroma_w);
366 1 s->planewidth[0] = s->planewidth[3] = inlink->w;
367
368 1 return 0;
369 }
370
371 1 static int config_output(AVFilterLink *outlink)
372 {
373 1 AVFilterContext *ctx = outlink->src;
374 1 HistogramContext *s = ctx->priv;
375 1 int ncomp = 0, i;
376
377
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!strcmp(ctx->filter->name, "thistogram"))
378 s->thistogram = 1;
379
380
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 for (i = 0; i < s->ncomp; i++) {
381
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if ((1 << i) & s->components)
382 3 ncomp++;
383 }
384
385
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (s->thistogram) {
386 if (!s->width)
387 s->width = ctx->inputs[0]->w;
388 outlink->w = s->width * FFMAX(ncomp * (s->display_mode == 1), 1);
389 outlink->h = s->histogram_size * FFMAX(ncomp * (s->display_mode == 2), 1);
390 } else {
391 1 outlink->w = s->histogram_size * FFMAX(ncomp * (s->display_mode == 1), 1);
392 1 outlink->h = (s->level_height + s->scale_height) * FFMAX(ncomp * (s->display_mode == 2), 1);
393 }
394
395 1 s->odesc = av_pix_fmt_desc_get(outlink->format);
396 1 s->dncomp = s->odesc->nb_components;
397 1 outlink->sample_aspect_ratio = (AVRational){1,1};
398
399 1 return 0;
400 }
401
402 50 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
403 {
404 50 HistogramContext *s = inlink->dst->priv;
405 50 AVFilterContext *ctx = inlink->dst;
406 50 AVFilterLink *outlink = ctx->outputs[0];
407 50 AVFrame *out = s->out;
408 int i, j, k, l, m;
409
410
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
50 if (!s->thistogram || !out) {
411 50 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
412
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
50 if (!out) {
413 av_frame_free(&in);
414 return AVERROR(ENOMEM);
415 }
416 50 s->out = out;
417
418
3/4
✓ Branch 0 taken 200 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 150 times.
✓ Branch 3 taken 50 times.
200 for (k = 0; k < 4 && out->data[k]; k++) {
419
4/4
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 50 times.
✓ Branch 2 taken 50 times.
✓ Branch 3 taken 50 times.
150 const int is_chroma = (k == 1 || k == 2);
420
2/2
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 50 times.
150 const int dst_h = AV_CEIL_RSHIFT(outlink->h, (is_chroma ? s->odesc->log2_chroma_h : 0));
421
2/2
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 50 times.
150 const int dst_w = AV_CEIL_RSHIFT(outlink->w, (is_chroma ? s->odesc->log2_chroma_w : 0));
422
423
1/2
✓ Branch 0 taken 150 times.
✗ Branch 1 not taken.
150 if (s->histogram_size <= 256) {
424
2/2
✓ Branch 0 taken 95400 times.
✓ Branch 1 taken 150 times.
95550 for (i = 0; i < dst_h ; i++)
425 95400 memset(out->data[s->odesc->comp[k].plane] +
426 95400 i * out->linesize[s->odesc->comp[k].plane],
427 95400 s->bg_color[0][k], dst_w);
428 } else {
429 const int mult = s->mult;
430
431 for (i = 0; i < dst_h ; i++)
432 for (j = 0; j < dst_w; j++)
433 AV_WN16(out->data[s->odesc->comp[k].plane] +
434 i * out->linesize[s->odesc->comp[k].plane] + j * 2,
435 s->bg_color[0][k] * mult);
436 }
437 }
438 }
439
440
2/2
✓ Branch 0 taken 150 times.
✓ Branch 1 taken 50 times.
200 for (m = 0, k = 0; k < s->ncomp; k++) {
441 150 const int p = s->desc->comp[k].plane;
442 150 const int max_value = s->histogram_size - 1 - s->start[p];
443 150 const int height = s->planeheight[p];
444 150 const int width = s->planewidth[p];
445 150 const int mid = s->mid;
446 double max_hval_log;
447 150 unsigned max_hval = 0;
448 int starty, startx;
449
450
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 150 times.
150 if (!((1 << k) & s->components))
451 continue;
452
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 150 times.
150 if (s->thistogram) {
453 starty = m * s->histogram_size * (s->display_mode == 2);
454 startx = m++ * s->width * (s->display_mode == 1);
455 } else {
456 150 startx = m * s->histogram_size * (s->display_mode == 1);
457 150 starty = m++ * (s->level_height + s->scale_height) * (s->display_mode == 2);
458 }
459
460
1/2
✓ Branch 0 taken 150 times.
✗ Branch 1 not taken.
150 if (s->histogram_size <= 256) {
461
2/2
✓ Branch 0 taken 28800 times.
✓ Branch 1 taken 150 times.
28950 for (i = 0; i < height; i++) {
462 28800 const uint8_t *src = in->data[p] + i * in->linesize[p];
463
2/2
✓ Branch 0 taken 7603200 times.
✓ Branch 1 taken 28800 times.
7632000 for (j = 0; j < width; j++)
464 7603200 s->histogram[src[j]]++;
465 }
466 } else {
467 for (i = 0; i < height; i++) {
468 const uint16_t *src = (const uint16_t *)(in->data[p] + i * in->linesize[p]);
469 for (j = 0; j < width; j++)
470 s->histogram[src[j]]++;
471 }
472 }
473
474
2/2
✓ Branch 0 taken 38400 times.
✓ Branch 1 taken 150 times.
38550 for (i = 0; i < s->histogram_size; i++)
475 38400 max_hval = FFMAX(max_hval, s->histogram[i]);
476 150 max_hval_log = log2(max_hval + 1);
477
478
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 150 times.
150 if (s->thistogram) {
479 const int bpp = 1 + (s->histogram_size > 256);
480 int minh = s->histogram_size - 1, maxh = 0;
481
482 if (s->slide == 2) {
483 s->x_pos = out->width - 1;
484 for (j = 0; j < outlink->h; j++) {
485 memmove(out->data[p] + j * out->linesize[p] ,
486 out->data[p] + j * out->linesize[p] + bpp,
487 (outlink->w - 1) * bpp);
488 }
489 } else if (s->slide == 3) {
490 s->x_pos = 0;
491 for (j = 0; j < outlink->h; j++) {
492 memmove(out->data[p] + j * out->linesize[p] + bpp,
493 out->data[p] + j * out->linesize[p],
494 (outlink->w - 1) * bpp);
495 }
496 }
497
498 for (int i = 0; i < s->histogram_size; i++) {
499 int idx = s->histogram_size - i - 1;
500 int value = s->start[p];
501
502 if (s->envelope && s->histogram[idx]) {
503 minh = FFMIN(minh, i);
504 maxh = FFMAX(maxh, i);
505 }
506
507 if (s->levels_mode)
508 value += lrint(max_value * (log2(s->histogram[idx] + 1) / max_hval_log));
509 else
510 value += lrint(max_value * s->histogram[idx] / (float)max_hval);
511
512 if (s->histogram_size <= 256) {
513 s->out->data[p][(i + starty) * s->out->linesize[p] + startx + s->x_pos] = value;
514 } else {
515 AV_WN16(s->out->data[p] + (i + starty) * s->out->linesize[p] + startx * 2 + s->x_pos * 2, value);
516 }
517 }
518
519 if (s->envelope) {
520 if (s->histogram_size <= 256) {
521 s->out->data[0][(minh + starty) * s->out->linesize[p] + startx + s->x_pos] = s->envelope_color[0];
522 s->out->data[0][(maxh + starty) * s->out->linesize[p] + startx + s->x_pos] = s->envelope_color[0];
523 if (s->dncomp >= 3) {
524 s->out->data[1][(minh + starty) * s->out->linesize[p] + startx + s->x_pos] = s->envelope_color[1];
525 s->out->data[2][(minh + starty) * s->out->linesize[p] + startx + s->x_pos] = s->envelope_color[2];
526 s->out->data[1][(maxh + starty) * s->out->linesize[p] + startx + s->x_pos] = s->envelope_color[1];
527 s->out->data[2][(maxh + starty) * s->out->linesize[p] + startx + s->x_pos] = s->envelope_color[2];
528 }
529 } else {
530 const int mult = s->mult;
531
532 AV_WN16(s->out->data[0] + (minh + starty) * s->out->linesize[p] + startx * 2 + s->x_pos * 2, s->envelope_color[0] * mult);
533 AV_WN16(s->out->data[0] + (maxh + starty) * s->out->linesize[p] + startx * 2 + s->x_pos * 2, s->envelope_color[0] * mult);
534 if (s->dncomp >= 3) {
535 AV_WN16(s->out->data[1] + (minh + starty) * s->out->linesize[p] + startx * 2 + s->x_pos * 2, s->envelope_color[1] * mult);
536 AV_WN16(s->out->data[2] + (minh + starty) * s->out->linesize[p] + startx * 2 + s->x_pos * 2, s->envelope_color[2] * mult);
537 AV_WN16(s->out->data[1] + (maxh + starty) * s->out->linesize[p] + startx * 2 + s->x_pos * 2, s->envelope_color[1] * mult);
538 AV_WN16(s->out->data[2] + (maxh + starty) * s->out->linesize[p] + startx * 2 + s->x_pos * 2, s->envelope_color[2] * mult);
539 }
540 }
541 }
542 } else {
543
2/2
✓ Branch 0 taken 38400 times.
✓ Branch 1 taken 150 times.
38550 for (i = 0; i < s->histogram_size; i++) {
544 int col_height;
545
546
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38400 times.
38400 if (s->levels_mode)
547 col_height = lrint(s->level_height * (1. - (log2(s->histogram[i] + 1) / max_hval_log)));
548 else
549 38400 col_height = s->level_height - (s->histogram[i] * (int64_t)s->level_height + max_hval - 1) / max_hval;
550
551
1/2
✓ Branch 0 taken 38400 times.
✗ Branch 1 not taken.
38400 if (s->histogram_size <= 256) {
552
2/2
✓ Branch 0 taken 2947198 times.
✓ Branch 1 taken 38400 times.
2985598 for (j = s->level_height - 1; j >= col_height; j--) {
553
1/2
✓ Branch 0 taken 2947198 times.
✗ Branch 1 not taken.
2947198 if (s->display_mode) {
554
2/2
✓ Branch 0 taken 8841594 times.
✓ Branch 1 taken 2947198 times.
11788792 for (l = 0; l < s->dncomp; l++)
555 8841594 out->data[l][(j + starty) * out->linesize[l] + startx + i] = s->fg_color[p][l];
556 } else {
557 out->data[p][(j + starty) * out->linesize[p] + startx + i] = 255;
558 }
559 }
560
1/2
✓ Branch 0 taken 38400 times.
✗ Branch 1 not taken.
38400 if (s->display_mode) {
561
2/2
✓ Branch 0 taken 4732802 times.
✓ Branch 1 taken 38400 times.
4771202 for (j = col_height - 1; j >= 0; j--) {
562
2/2
✓ Branch 0 taken 14198406 times.
✓ Branch 1 taken 4732802 times.
18931208 for (l = 0; l < s->dncomp; l++)
563 14198406 out->data[l][(j + starty) * out->linesize[l] + startx + i] = s->bg_color[p][l];
564 }
565 }
566
2/2
✓ Branch 0 taken 460800 times.
✓ Branch 1 taken 38400 times.
499200 for (j = s->level_height + s->scale_height - 1; j >= s->level_height; j--)
567
2/2
✓ Branch 0 taken 1382400 times.
✓ Branch 1 taken 460800 times.
1843200 for (l = 0; l < s->dncomp; l++)
568
2/2
✓ Branch 0 taken 460800 times.
✓ Branch 1 taken 921600 times.
1382400 out->data[l][(j + starty) * out->linesize[l] + startx + i] = p == l ? i : mid;
569 } else {
570 const int mult = s->mult;
571
572 for (j = s->level_height - 1; j >= col_height; j--) {
573 if (s->display_mode) {
574 for (l = 0; l < s->dncomp; l++)
575 AV_WN16(out->data[l] + (j + starty) * out->linesize[l] + startx * 2 + i * 2, s->fg_color[p][l] * mult);
576 } else {
577 AV_WN16(out->data[p] + (j + starty) * out->linesize[p] + startx * 2 + i * 2, 255 * mult);
578 }
579 }
580 if (s->display_mode) {
581 for (j = col_height - 1; j >= 0; j--) {
582 for (l = 0; l < s->dncomp; l++)
583 AV_WN16(out->data[l] + (j + starty) * out->linesize[l] + startx * 2 + i * 2, s->bg_color[p][l] * mult);
584 }
585 }
586 for (j = s->level_height + s->scale_height - 1; j >= s->level_height; j--)
587 for (l = 0; l < s->dncomp; l++)
588 AV_WN16(out->data[l] + (j + starty) * out->linesize[l] + startx * 2 + i * 2, p == l ? i : mid * mult);
589 }
590 }
591 }
592
593 150 memset(s->histogram, 0, s->histogram_size * sizeof(unsigned));
594 }
595
596 50 av_frame_copy_props(out, in);
597 50 av_frame_free(&in);
598 50 s->x_pos++;
599
1/2
✓ Branch 0 taken 50 times.
✗ Branch 1 not taken.
50 if (s->x_pos >= s->width) {
600 50 s->x_pos = 0;
601
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
50 if (s->thistogram && (s->slide == 4 || s->slide == 0)) {
602 s->out = NULL;
603 goto end;
604 }
605 } else if (s->thistogram && s->slide == 4) {
606 return 0;
607 }
608
609
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
50 if (s->thistogram) {
610 AVFrame *clone = av_frame_clone(out);
611
612 if (!clone)
613 return AVERROR(ENOMEM);
614 return ff_filter_frame(outlink, clone);
615 }
616 50 end:
617 50 return ff_filter_frame(outlink, out);
618 }
619
620 static const AVFilterPad inputs[] = {
621 {
622 .name = "default",
623 .type = AVMEDIA_TYPE_VIDEO,
624 .filter_frame = filter_frame,
625 .config_props = config_input,
626 },
627 };
628
629 static const AVFilterPad outputs[] = {
630 {
631 .name = "default",
632 .type = AVMEDIA_TYPE_VIDEO,
633 .config_props = config_output,
634 },
635 };
636
637 #if CONFIG_HISTOGRAM_FILTER
638
639 const AVFilter ff_vf_histogram = {
640 .name = "histogram",
641 .description = NULL_IF_CONFIG_SMALL("Compute and draw a histogram."),
642 .priv_size = sizeof(HistogramContext),
643 FILTER_INPUTS(inputs),
644 FILTER_OUTPUTS(outputs),
645 FILTER_QUERY_FUNC(query_formats),
646 .priv_class = &histogram_class,
647 };
648
649 #endif /* CONFIG_HISTOGRAM_FILTER */
650
651 #if CONFIG_THISTOGRAM_FILTER
652
653 static av_cold void uninit(AVFilterContext *ctx)
654 {
655 HistogramContext *s = ctx->priv;
656
657 av_frame_free(&s->out);
658 }
659
660 static const AVOption thistogram_options[] = {
661 { "width", "set width", OFFSET(width), AV_OPT_TYPE_INT, {.i64=0}, 0, 8192, FLAGS},
662 { "w", "set width", OFFSET(width), AV_OPT_TYPE_INT, {.i64=0}, 0, 8192, FLAGS},
663 COMMON_OPTIONS
664 { "bgopacity", "set background opacity", OFFSET(bgopacity), AV_OPT_TYPE_FLOAT, {.dbl=0.9}, 0, 1, FLAGS},
665 { "b", "set background opacity", OFFSET(bgopacity), AV_OPT_TYPE_FLOAT, {.dbl=0.9}, 0, 1, FLAGS},
666 { "envelope", "display envelope", OFFSET(envelope), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
667 { "e", "display envelope", OFFSET(envelope), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
668 { "ecolor", "set envelope color", OFFSET(envelope_rgba), AV_OPT_TYPE_COLOR, {.str="gold"}, 0, 0, FLAGS },
669 { "ec", "set envelope color", OFFSET(envelope_rgba), AV_OPT_TYPE_COLOR, {.str="gold"}, 0, 0, FLAGS },
670 { "slide", "set slide mode", OFFSET(slide), AV_OPT_TYPE_INT, {.i64=1}, 0, 4, FLAGS, .unit = "slide" },
671 {"frame", "draw new frames", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "slide"},
672 {"replace", "replace old columns with new", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "slide"},
673 {"scroll", "scroll from right to left", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, .unit = "slide"},
674 {"rscroll", "scroll from left to right", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, FLAGS, .unit = "slide"},
675 {"picture", "display graph in single frame", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, FLAGS, .unit = "slide"},
676 { NULL }
677 };
678
679 AVFILTER_DEFINE_CLASS(thistogram);
680
681 const AVFilter ff_vf_thistogram = {
682 .name = "thistogram",
683 .description = NULL_IF_CONFIG_SMALL("Compute and draw a temporal histogram."),
684 .priv_size = sizeof(HistogramContext),
685 FILTER_INPUTS(inputs),
686 FILTER_OUTPUTS(outputs),
687 FILTER_QUERY_FUNC(query_formats),
688 .uninit = uninit,
689 .priv_class = &thistogram_class,
690 };
691
692 #endif /* CONFIG_THISTOGRAM_FILTER */
693