• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, "display_mode"}, \
71     { "d",            "set display mode", OFFSET(display_mode), AV_OPT_TYPE_INT, {.i64=2}, 0, 2, FLAGS, "display_mode"}, \
72         { "overlay", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "display_mode" }, \
73         { "parade",  NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "display_mode" }, \
74         { "stack",   NULL, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "display_mode" }, \
75     { "levels_mode", "set levels mode", OFFSET(levels_mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "levels_mode"}, \
76     { "m",           "set levels mode", OFFSET(levels_mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "levels_mode"}, \
77         { "linear",      NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "levels_mode" }, \
78         { "logarithmic", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "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, "colors_mode"},
91     { "l",           "set colors mode", OFFSET(colors_mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 9, FLAGS, "colors_mode"},
92         { "whiteonblack", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "colors_mode" },
93         { "blackonwhite", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "colors_mode" },
94         { "whiteongray",  NULL, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "colors_mode" },
95         { "blackongray",  NULL, 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, FLAGS, "colors_mode" },
96         { "coloronblack", NULL, 0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, FLAGS, "colors_mode" },
97         { "coloronwhite", NULL, 0, AV_OPT_TYPE_CONST, {.i64=5}, 0, 0, FLAGS, "colors_mode" },
98         { "colorongray" , NULL, 0, AV_OPT_TYPE_CONST, {.i64=6}, 0, 0, FLAGS, "colors_mode" },
99         { "blackoncolor", NULL, 0, AV_OPT_TYPE_CONST, {.i64=7}, 0, 0, FLAGS, "colors_mode" },
100         { "whiteoncolor", NULL, 0, AV_OPT_TYPE_CONST, {.i64=8}, 0, 0, FLAGS, "colors_mode" },
101         { "grayoncolor" , NULL, 0, AV_OPT_TYPE_CONST, {.i64=9}, 0, 0, FLAGS, "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 
query_formats(AVFilterContext * ctx)166 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     if (!ctx->inputs[0]->incfg.formats ||
175         !ctx->inputs[0]->incfg.formats->nb_formats) {
176         return AVERROR(EAGAIN);
177     }
178 
179     if (!ctx->inputs[0]->outcfg.formats)
180         if ((ret = ff_formats_ref(ff_make_format_list(levels_in_pix_fmts), &ctx->inputs[0]->outcfg.formats)) < 0)
181             return ret;
182     avff = ctx->inputs[0]->incfg.formats;
183     desc = av_pix_fmt_desc_get(avff->formats[0]);
184     rgb = desc->flags & AV_PIX_FMT_FLAG_RGB;
185     bits = desc->comp[0].depth;
186     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     if (rgb && bits == 8)
194         out_pix_fmts = levels_out_rgb8_pix_fmts;
195     else if (rgb && bits == 9)
196         out_pix_fmts = levels_out_rgb9_pix_fmts;
197     else if (rgb && bits == 10)
198         out_pix_fmts = levels_out_rgb10_pix_fmts;
199     else if (rgb && bits == 12)
200         out_pix_fmts = levels_out_rgb12_pix_fmts;
201     else if (bits == 8)
202         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     if ((ret = ff_formats_ref(ff_make_format_list(out_pix_fmts), &ctx->outputs[0]->incfg.formats)) < 0)
212         return ret;
213 
214     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 
config_input(AVFilterLink * inlink)230 static int config_input(AVFilterLink *inlink)
231 {
232     HistogramContext *s = inlink->dst->priv;
233     int rgb = 0;
234 
235     s->desc  = av_pix_fmt_desc_get(inlink->format);
236     s->ncomp = s->desc->nb_components;
237     s->histogram_size = 1 << s->desc->comp[0].depth;
238     s->mult = s->histogram_size / 256;
239 
240     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     default:
255         s->mid = 127;
256         memcpy(s->bg_color[0], black_yuva_color, 4);
257         memcpy(s->fg_color[0], white_yuva_color, 4);
258         s->start[0] = s->start[3] = 0;
259         s->start[1] = s->start[2] = s->histogram_size / 2;
260         s->envelope_color[0] = RGB_TO_Y_BT709(s->envelope_rgba[0], s->envelope_rgba[1], s->envelope_rgba[2]);
261         s->envelope_color[1] = RGB_TO_U_BT709(s->envelope_rgba[0], s->envelope_rgba[1], s->envelope_rgba[2], 0);
262         s->envelope_color[2] = RGB_TO_V_BT709(s->envelope_rgba[0], s->envelope_rgba[1], s->envelope_rgba[2], 0);
263         s->envelope_color[3] = s->envelope_rgba[3];
264     }
265 
266     for (int i = 1; i < 4; i++) {
267         memcpy(s->fg_color[i], s->fg_color[0], 4);
268         memcpy(s->bg_color[i], s->bg_color[0], 4);
269     }
270 
271     if (s->display_mode) {
272         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         } else if (s->colors_mode == 2) {
277             for (int i = 0; i < 4; i++)
278                 memcpy(s->bg_color[i], gray_color, 4);
279         } 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         } 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         } 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         } 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         } 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         } 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         } 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     for (int i = 0; i < 4; i++) {
359         s->fg_color[i][3] = s->fgopacity * 255;
360         s->bg_color[i][3] = s->bgopacity * 255;
361     }
362 
363     s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, s->desc->log2_chroma_h);
364     s->planeheight[0] = s->planeheight[3] = inlink->h;
365     s->planewidth[1]  = s->planewidth[2]  = AV_CEIL_RSHIFT(inlink->w, s->desc->log2_chroma_w);
366     s->planewidth[0]  = s->planewidth[3]  = inlink->w;
367 
368     return 0;
369 }
370 
config_output(AVFilterLink * outlink)371 static int config_output(AVFilterLink *outlink)
372 {
373     AVFilterContext *ctx = outlink->src;
374     HistogramContext *s = ctx->priv;
375     int ncomp = 0, i;
376 
377     if (!strcmp(ctx->filter->name, "thistogram"))
378         s->thistogram = 1;
379 
380     for (i = 0; i < s->ncomp; i++) {
381         if ((1 << i) & s->components)
382             ncomp++;
383     }
384 
385     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         outlink->w = s->histogram_size * FFMAX(ncomp * (s->display_mode == 1), 1);
392         outlink->h = (s->level_height + s->scale_height) * FFMAX(ncomp * (s->display_mode == 2), 1);
393     }
394 
395     s->odesc = av_pix_fmt_desc_get(outlink->format);
396     s->dncomp = s->odesc->nb_components;
397     outlink->sample_aspect_ratio = (AVRational){1,1};
398 
399     return 0;
400 }
401 
filter_frame(AVFilterLink * inlink,AVFrame * in)402 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
403 {
404     HistogramContext *s   = inlink->dst->priv;
405     AVFilterContext *ctx  = inlink->dst;
406     AVFilterLink *outlink = ctx->outputs[0];
407     AVFrame *out = s->out;
408     int i, j, k, l, m;
409 
410     if (!s->thistogram || !out) {
411         out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
412         if (!out) {
413             av_frame_free(&in);
414             return AVERROR(ENOMEM);
415         }
416         s->out = out;
417 
418         for (k = 0; k < 4 && out->data[k]; k++) {
419             const int is_chroma = (k == 1 || k == 2);
420             const int dst_h = AV_CEIL_RSHIFT(outlink->h, (is_chroma ? s->odesc->log2_chroma_h : 0));
421             const int dst_w = AV_CEIL_RSHIFT(outlink->w, (is_chroma ? s->odesc->log2_chroma_w : 0));
422 
423             if (s->histogram_size <= 256) {
424                 for (i = 0; i < dst_h ; i++)
425                     memset(out->data[s->odesc->comp[k].plane] +
426                            i * out->linesize[s->odesc->comp[k].plane],
427                            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     for (m = 0, k = 0; k < s->ncomp; k++) {
441         const int p = s->desc->comp[k].plane;
442         const int max_value = s->histogram_size - 1 - s->start[p];
443         const int height = s->planeheight[p];
444         const int width = s->planewidth[p];
445         const int mid = s->mid;
446         double max_hval_log;
447         unsigned max_hval = 0;
448         int starty, startx;
449 
450         if (!((1 << k) & s->components))
451             continue;
452         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             startx = m * s->histogram_size * (s->display_mode == 1);
457             starty = m++ * (s->level_height + s->scale_height) * (s->display_mode == 2);
458         }
459 
460         if (s->histogram_size <= 256) {
461             for (i = 0; i < height; i++) {
462                 const uint8_t *src = in->data[p] + i * in->linesize[p];
463                 for (j = 0; j < width; j++)
464                     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         for (i = 0; i < s->histogram_size; i++)
475             max_hval = FFMAX(max_hval, s->histogram[i]);
476         max_hval_log = log2(max_hval + 1);
477 
478         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             for (i = 0; i < s->histogram_size; i++) {
544                 int col_height;
545 
546                 if (s->levels_mode)
547                     col_height = lrint(s->level_height * (1. - (log2(s->histogram[i] + 1) / max_hval_log)));
548                 else
549                     col_height = s->level_height - (s->histogram[i] * (int64_t)s->level_height + max_hval - 1) / max_hval;
550 
551                 if (s->histogram_size <= 256) {
552                     for (j = s->level_height - 1; j >= col_height; j--) {
553                         if (s->display_mode) {
554                             for (l = 0; l < s->dncomp; l++)
555                                 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                     if (s->display_mode) {
561                         for (j = col_height - 1; j >= 0; j--) {
562                             for (l = 0; l < s->dncomp; l++)
563                                 out->data[l][(j + starty) * out->linesize[l] + startx + i] = s->bg_color[p][l];
564                         }
565                     }
566                     for (j = s->level_height + s->scale_height - 1; j >= s->level_height; j--)
567                         for (l = 0; l < s->dncomp; l++)
568                             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         memset(s->histogram, 0, s->histogram_size * sizeof(unsigned));
594     }
595 
596     out->pts = in->pts;
597     av_frame_free(&in);
598     s->x_pos++;
599     if (s->x_pos >= s->width) {
600         s->x_pos = 0;
601         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     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 end:
617     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 
uninit(AVFilterContext * ctx)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, "slide" },
671         {"frame",   "draw new frames",               OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "slide"},
672         {"replace", "replace old columns with new",  OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "slide"},
673         {"scroll",  "scroll from right to left",     OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "slide"},
674         {"rscroll", "scroll from left to right",     OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, FLAGS, "slide"},
675         {"picture", "display graph in single frame", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, FLAGS, "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