2 * Copyright (c) 2015 Paul B Mahol
4 * This file is part of FFmpeg.
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.
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.
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
21 #include "libavutil/avassert.h"
22 #include "libavutil/opt.h"
23 #include "libavutil/parseutils.h"
24 #include "libavutil/pixdesc.h"
30 enum VectorscopeMode {
39 typedef struct VectorscopeContext {
44 const uint8_t *bg_color;
51 uint8_t peak[256][256];
54 #define OFFSET(x) offsetof(VectorscopeContext, x)
55 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
57 static const AVOption vectorscope_options[] = {
58 { "mode", "set vectorscope mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, MODE_NB-1, FLAGS, "mode"},
59 { "m", "set vectorscope mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, MODE_NB-1, FLAGS, "mode"},
60 { "gray", 0, 0, AV_OPT_TYPE_CONST, {.i64=GRAY}, 0, 0, FLAGS, "mode" },
61 { "color", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR}, 0, 0, FLAGS, "mode" },
62 { "color2", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR2}, 0, 0, FLAGS, "mode" },
63 { "color3", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR3}, 0, 0, FLAGS, "mode" },
64 { "color4", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR4}, 0, 0, FLAGS, "mode" },
65 { "x", "set color component on X axis", OFFSET(x), AV_OPT_TYPE_INT, {.i64=1}, 0, 2, FLAGS},
66 { "y", "set color component on Y axis", OFFSET(y), AV_OPT_TYPE_INT, {.i64=2}, 0, 2, FLAGS},
67 { "intensity", "set intensity", OFFSET(fintensity), AV_OPT_TYPE_FLOAT, {.dbl=0.004}, 0, 1, FLAGS},
68 { "i", "set intensity", OFFSET(fintensity), AV_OPT_TYPE_FLOAT, {.dbl=0.004}, 0, 1, FLAGS},
69 { "envelope", "set envelope", OFFSET(envelope), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, FLAGS, "envelope"},
70 { "e", "set envelope", OFFSET(envelope), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, FLAGS, "envelope"},
71 { "none", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "envelope" },
72 { "instant", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "envelope" },
73 { "peak", 0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "envelope" },
74 { "peak+instant", 0, 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, FLAGS, "envelope" },
78 AVFILTER_DEFINE_CLASS(vectorscope);
80 static const enum AVPixelFormat out_yuv_pix_fmts[] = {
81 AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P,
85 static const enum AVPixelFormat out_rgb_pix_fmts[] = {
86 AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP,
90 static const enum AVPixelFormat in1_pix_fmts[] = {
91 AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P,
92 AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP,
96 static const enum AVPixelFormat in2_pix_fmts[] = {
97 AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVJ420P,
98 AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVJ422P,
99 AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUVJ411P,
100 AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV410P,
101 AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P,
102 AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP,
106 static int query_formats(AVFilterContext *ctx)
108 VectorscopeContext *s = ctx->priv;
109 const enum AVPixelFormat *out_pix_fmts;
110 const AVPixFmtDescriptor *desc;
111 AVFilterFormats *avff;
114 if (!ctx->inputs[0]->in_formats ||
115 !ctx->inputs[0]->in_formats->nb_formats) {
116 return AVERROR(EAGAIN);
119 if (!ctx->inputs[0]->out_formats) {
120 const enum AVPixelFormat *in_pix_fmts;
122 if ((s->x == 1 && s->y == 2) || (s->x == 2 && s->y == 1))
123 in_pix_fmts = in2_pix_fmts;
125 in_pix_fmts = in1_pix_fmts;
126 ff_formats_ref(ff_make_format_list(in_pix_fmts), &ctx->inputs[0]->out_formats);
129 avff = ctx->inputs[0]->in_formats;
130 desc = av_pix_fmt_desc_get(avff->formats[0]);
131 rgb = desc->flags & AV_PIX_FMT_FLAG_RGB;
132 for (i = 1; i < avff->nb_formats; i++) {
133 desc = av_pix_fmt_desc_get(avff->formats[i]);
134 if (rgb != (desc->flags & AV_PIX_FMT_FLAG_RGB))
135 return AVERROR(EAGAIN);
139 out_pix_fmts = out_rgb_pix_fmts;
141 out_pix_fmts = out_yuv_pix_fmts;
142 ff_formats_ref(ff_make_format_list(out_pix_fmts), &ctx->outputs[0]->in_formats);
147 static const uint8_t black_yuva_color[4] = { 0, 127, 127, 0 };
148 static const uint8_t black_gbrp_color[4] = { 0, 0, 0, 0 };
150 static int config_input(AVFilterLink *inlink)
152 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
153 VectorscopeContext *s = inlink->dst->priv;
155 s->is_yuv = !(desc->flags & AV_PIX_FMT_FLAG_RGB);
157 if (s->mode == GRAY && s->is_yuv)
160 if ((s->x == 1 && s->y == 2) || (s->x == 2 && s->y == 1))
162 else if ((s->x == 0 && s->y == 2) || (s->x == 2 && s->y == 0))
164 else if ((s->x == 0 && s->y == 1) || (s->x == 1 && s->y == 0))
168 switch (inlink->format) {
169 case AV_PIX_FMT_GBRAP:
170 case AV_PIX_FMT_GBRP:
171 s->bg_color = black_gbrp_color;
174 s->bg_color = black_yuva_color;
177 s->hsub = desc->log2_chroma_w;
178 s->vsub = desc->log2_chroma_h;
179 s->planeheight[1] = s->planeheight[2] = FF_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
180 s->planeheight[0] = s->planeheight[3] = inlink->h;
181 s->planewidth[1] = s->planewidth[2] = FF_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
182 s->planewidth[0] = s->planewidth[3] = inlink->w;
187 static int config_output(AVFilterLink *outlink)
189 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format);
190 const int depth = desc->comp[0].depth_minus1 + 1;
191 VectorscopeContext *s = outlink->src->priv;
193 s->intensity = s->fintensity * ((1 << depth) - 1);
194 outlink->h = outlink->w = 1 << depth;
195 outlink->sample_aspect_ratio = (AVRational){1,1};
199 static void envelope_instant(VectorscopeContext *s, AVFrame *out)
201 const int dlinesize = out->linesize[0];
202 uint8_t *dpd = s->mode == COLOR || !s->is_yuv ? out->data[s->pd] : out->data[0];
205 for (i = 0; i < out->height; i++) {
206 for (j = 0; j < out->width; j++) {
207 const int pos = i * dlinesize + j;
208 const int poa = (i - 1) * dlinesize + j;
209 const int pob = (i + 1) * dlinesize + j;
211 if (dpd[pos] && (((!j || !dpd[pos - 1]) || ((j == (out->width - 1)) || !dpd[pos + 1]))
212 || ((!i || !dpd[poa]) || ((i == (out->height - 1)) || !dpd[pob])))) {
219 static void envelope_peak(VectorscopeContext *s, AVFrame *out)
221 const int dlinesize = out->linesize[0];
222 uint8_t *dpd = s->mode == COLOR || !s->is_yuv ? out->data[s->pd] : out->data[0];
225 for (i = 0; i < out->height; i++) {
226 for (j = 0; j < out->width; j++) {
227 const int pos = i * dlinesize + j;
234 if (s->envelope == 3)
235 envelope_instant(s, out);
237 for (i = 0; i < out->height; i++) {
238 for (j = 0; j < out->width; j++) {
239 const int pos = i * dlinesize + j;
241 if (s->peak[i][j] && (((!j || !s->peak[i][j-1]) || ((j == (out->width - 1)) || !s->peak[i][j + 1]))
242 || ((!i || !s->peak[i-1][j]) || ((i == (out->height - 1)) || !s->peak[i + 1][j])))) {
249 static void envelope(VectorscopeContext *s, AVFrame *out)
253 } else if (s->envelope == 1) {
254 envelope_instant(s, out);
256 envelope_peak(s, out);
260 static void vectorscope(VectorscopeContext *s, AVFrame *in, AVFrame *out, int pd)
262 const uint8_t * const *src = (const uint8_t * const *)in->data;
263 const int slinesizex = in->linesize[s->x];
264 const int slinesizey = in->linesize[s->y];
265 const int slinesized = in->linesize[pd];
266 const int dlinesize = out->linesize[0];
267 const int intensity = s->intensity;
268 const int px = s->x, py = s->y;
269 const int h = s->planeheight[py];
270 const int w = s->planewidth[px];
271 const uint8_t *spx = src[px];
272 const uint8_t *spy = src[py];
273 const uint8_t *spd = src[pd];
274 const int hsub = s->hsub;
275 const int vsub = s->vsub;
276 uint8_t **dst = out->data;
277 uint8_t *dpx = dst[px];
278 uint8_t *dpy = dst[py];
279 uint8_t *dpd = dst[pd];
286 for (i = 0; i < h; i++) {
287 const int iwx = i * slinesizex;
288 const int iwy = i * slinesizey;
289 for (j = 0; j < w; j++) {
290 const int x = spx[iwx + j];
291 const int y = spy[iwy + j];
292 const int pos = y * dlinesize + x;
294 dpd[pos] = FFMIN(dpd[pos] + intensity, 255);
300 for (i = 0; i < h; i++) {
301 const int iwx = i * slinesizex;
302 const int iwy = i * slinesizey;
303 for (j = 0; j < w; j++) {
304 const int x = spx[iwx + j];
305 const int y = spy[iwy + j];
306 const int pos = y * dlinesize + x;
308 dst[0][pos] = FFMIN(dst[0][pos] + intensity, 255);
309 dst[1][pos] = FFMIN(dst[1][pos] + intensity, 255);
310 dst[2][pos] = FFMIN(dst[2][pos] + intensity, 255);
319 for (i = 0; i < h; i++) {
320 const int iw1 = i * slinesizex;
321 const int iw2 = i * slinesizey;
322 for (j = 0; j < w; j++) {
323 const int x = spx[iw1 + j];
324 const int y = spy[iw2 + j];
325 const int pos = y * dlinesize + x;
328 dpd[pos] = FFABS(128 - x) + FFABS(128 - y);
336 for (i = 0; i < h; i++) {
337 const int iw1 = i * slinesizex;
338 const int iw2 = i * slinesizey;
339 for (j = 0; j < w; j++) {
340 const int x = spx[iw1 + j];
341 const int y = spy[iw2 + j];
342 const int pos = y * dlinesize + x;
345 dpd[pos] = FFMIN(x + y, 255);
355 for (i = 0; i < h; i++) {
356 const int iw1 = i * slinesizex;
357 const int iw2 = i * slinesizey;
358 for (j = 0; j < w; j++) {
359 const int x = spx[iw1 + j];
360 const int y = spy[iw2 + j];
361 const int pos = y * dlinesize + x;
363 dpd[pos] = FFMIN(255, dpd[pos] + intensity);
372 for (i = 0; i < in->height; i++) {
373 const int iwx = (i >> vsub) * slinesizex;
374 const int iwy = (i >> vsub) * slinesizey;
375 const int iwd = i * slinesized;
376 for (j = 0; j < in->width; j++) {
377 const int x = spx[iwx + (j >> hsub)];
378 const int y = spy[iwy + (j >> hsub)];
379 const int pos = y * dlinesize + x;
381 dpd[pos] = FFMAX(spd[iwd + j], dpd[pos]);
395 if (s->mode == COLOR) {
396 for (i = 0; i < out->height; i++) {
397 for (j = 0; j < out->width; j++) {
398 if (!dpd[i * out->linesize[pd] + j]) {
399 dpx[i * out->linesize[px] + j] = j;
400 dpy[i * out->linesize[py] + j] = i;
401 dpd[i * out->linesize[pd] + j] = 128;
408 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
410 AVFilterContext *ctx = inlink->dst;
411 VectorscopeContext *s = ctx->priv;
412 AVFilterLink *outlink = ctx->outputs[0];
417 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
420 return AVERROR(ENOMEM);
425 for (k = 0; k < 4 && dst[k]; k++)
426 for (i = 0; i < outlink->h ; i++)
427 memset(dst[k] + i * out->linesize[k],
428 s->mode == COLOR && k == s->pd ? 0 : s->bg_color[k], outlink->w);
430 vectorscope(s, in, out, s->pd);
433 return ff_filter_frame(outlink, out);
436 static const AVFilterPad inputs[] = {
439 .type = AVMEDIA_TYPE_VIDEO,
440 .filter_frame = filter_frame,
441 .config_props = config_input,
446 static const AVFilterPad outputs[] = {
449 .type = AVMEDIA_TYPE_VIDEO,
450 .config_props = config_output,
455 AVFilter ff_vf_vectorscope = {
456 .name = "vectorscope",
457 .description = NULL_IF_CONFIG_SMALL("Video vectorscope."),
458 .priv_size = sizeof(VectorscopeContext),
459 .priv_class = &vectorscope_class,
460 .query_formats = query_formats,