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 {
43 const uint8_t *bg_color;
49 uint8_t peak[256][256];
52 #define OFFSET(x) offsetof(VectorscopeContext, x)
53 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
55 static const AVOption vectorscope_options[] = {
56 { "mode", "set vectorscope mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, MODE_NB-1, FLAGS, "mode"},
57 { "m", "set vectorscope mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, MODE_NB-1, FLAGS, "mode"},
58 { "gray", 0, 0, AV_OPT_TYPE_CONST, {.i64=GRAY}, 0, 0, FLAGS, "mode" },
59 { "color", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR}, 0, 0, FLAGS, "mode" },
60 { "color2", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR2}, 0, 0, FLAGS, "mode" },
61 { "color3", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR3}, 0, 0, FLAGS, "mode" },
62 { "color4", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR4}, 0, 0, FLAGS, "mode" },
63 { "x", "set color component on X axis", OFFSET(x), AV_OPT_TYPE_INT, {.i64=1}, 0, 2, FLAGS},
64 { "y", "set color component on Y axis", OFFSET(y), AV_OPT_TYPE_INT, {.i64=2}, 0, 2, FLAGS},
65 { "intensity", "set intensity", OFFSET(intensity), AV_OPT_TYPE_INT, {.i64=1}, 1, 255, FLAGS},
66 { "i", "set intensity", OFFSET(intensity), AV_OPT_TYPE_INT, {.i64=1}, 1, 255, FLAGS},
67 { "envelope", "set envelope", OFFSET(envelope), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, FLAGS, "envelope"},
68 { "e", "set envelope", OFFSET(envelope), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, FLAGS, "envelope"},
69 { "none", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "envelope" },
70 { "instant", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "envelope" },
71 { "peak", 0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "envelope" },
72 { "peak+instant", 0, 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, FLAGS, "envelope" },
76 AVFILTER_DEFINE_CLASS(vectorscope);
78 static const enum AVPixelFormat out_yuv_pix_fmts[] = {
79 AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P,
83 static const enum AVPixelFormat out_rgb_pix_fmts[] = {
84 AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP,
88 static const enum AVPixelFormat in1_pix_fmts[] = {
89 AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P,
90 AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP,
94 static const enum AVPixelFormat in2_pix_fmts[] = {
95 AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVJ420P,
96 AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVJ422P,
97 AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUVJ411P,
98 AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV410P,
99 AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P,
100 AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP,
104 static int query_formats(AVFilterContext *ctx)
106 VectorscopeContext *s = ctx->priv;
107 const enum AVPixelFormat *out_pix_fmts;
108 const AVPixFmtDescriptor *desc;
109 AVFilterFormats *avff;
112 if (!ctx->inputs[0]->in_formats ||
113 !ctx->inputs[0]->in_formats->nb_formats) {
114 return AVERROR(EAGAIN);
117 if (!ctx->inputs[0]->out_formats) {
118 const enum AVPixelFormat *in_pix_fmts;
120 if (((s->x == 1 && s->y == 2) || (s->x == 2 && s->y == 1)) &&
122 in_pix_fmts = in2_pix_fmts;
124 in_pix_fmts = in1_pix_fmts;
125 ff_formats_ref(ff_make_format_list(in_pix_fmts), &ctx->inputs[0]->out_formats);
128 avff = ctx->inputs[0]->in_formats;
129 desc = av_pix_fmt_desc_get(avff->formats[0]);
130 rgb = desc->flags & AV_PIX_FMT_FLAG_RGB;
131 for (i = 1; i < avff->nb_formats; i++) {
132 desc = av_pix_fmt_desc_get(avff->formats[i]);
133 if (rgb != (desc->flags & AV_PIX_FMT_FLAG_RGB))
134 return AVERROR(EAGAIN);
138 out_pix_fmts = out_rgb_pix_fmts;
140 out_pix_fmts = out_yuv_pix_fmts;
141 ff_formats_ref(ff_make_format_list(out_pix_fmts), &ctx->outputs[0]->in_formats);
146 static const uint8_t black_yuva_color[4] = { 0, 127, 127, 0 };
147 static const uint8_t black_gbrp_color[4] = { 0, 0, 0, 0 };
149 static int config_input(AVFilterLink *inlink)
151 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
152 VectorscopeContext *s = inlink->dst->priv;
154 s->is_yuv = !(desc->flags & AV_PIX_FMT_FLAG_RGB);
156 if (s->mode == GRAY && s->is_yuv)
159 if ((s->x == 1 && s->y == 2) || (s->x == 2 && s->y == 1))
161 else if ((s->x == 0 && s->y == 2) || (s->x == 2 && s->y == 0))
163 else if ((s->x == 0 && s->y == 1) || (s->x == 1 && s->y == 0))
167 switch (inlink->format) {
168 case AV_PIX_FMT_GBRAP:
169 case AV_PIX_FMT_GBRP:
170 s->bg_color = black_gbrp_color;
173 s->bg_color = black_yuva_color;
176 s->planeheight[1] = s->planeheight[2] = FF_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
177 s->planeheight[0] = s->planeheight[3] = inlink->h;
178 s->planewidth[1] = s->planewidth[2] = FF_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
179 s->planewidth[0] = s->planewidth[3] = inlink->w;
184 static int config_output(AVFilterLink *outlink)
186 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format);
187 int depth = desc->comp[0].depth_minus1 + 1;
189 outlink->h = outlink->w = 1 << depth;
190 outlink->sample_aspect_ratio = (AVRational){1,1};
194 static void envelope_instant(VectorscopeContext *s, AVFrame *out)
196 const int dlinesize = out->linesize[0];
197 uint8_t *dpd = s->mode == COLOR || !s->is_yuv ? out->data[s->pd] : out->data[0];
200 for (i = 0; i < out->height; i++) {
201 for (j = 0; j < out->width; j++) {
202 int pos = i * dlinesize + j;
203 int poa = (i - 1) * dlinesize + j;
204 int pob = (i + 1) * dlinesize + j;
206 if (dpd[pos] && (((!j || !dpd[pos - 1]) || ((j == (out->width - 1)) || !dpd[pos + 1]))
207 || ((!i || !dpd[poa]) || ((i == (out->height - 1)) || !dpd[pob])))) {
214 static void envelope_peak(VectorscopeContext *s, AVFrame *out)
216 const int dlinesize = out->linesize[0];
217 uint8_t *dpd = s->mode == COLOR || !s->is_yuv ? out->data[s->pd] : out->data[0];
220 for (i = 0; i < out->height; i++) {
221 for (j = 0; j < out->width; j++) {
222 int pos = i * dlinesize + j;
229 if (s->envelope == 3)
230 envelope_instant(s, out);
232 for (i = 0; i < out->height; i++) {
233 for (j = 0; j < out->width; j++) {
234 int pos = i * dlinesize + j;
236 if (s->peak[i][j] && (((!j || !s->peak[i][j-1]) || ((j == (out->width - 1)) || !s->peak[i][j + 1]))
237 || ((!i || !s->peak[i-1][j]) || ((i == (out->height - 1)) || !s->peak[i + 1][j])))) {
244 static void envelope(VectorscopeContext *s, AVFrame *out)
248 } else if (s->envelope == 1) {
249 envelope_instant(s, out);
251 envelope_peak(s, out);
255 static void vectorscope(VectorscopeContext *s, AVFrame *in, AVFrame *out, int pd)
257 const uint8_t * const *src = (const uint8_t * const *)in->data;
258 const int slinesizex = in->linesize[s->x];
259 const int slinesizey = in->linesize[s->y];
260 const int slinesized = in->linesize[pd];
261 const int dlinesize = out->linesize[0];
262 const int intensity = s->intensity;
263 int i, j, px = s->x, py = s->y;
264 const int h = s->planeheight[py];
265 const int w = s->planewidth[px];
266 const uint8_t *spx = src[px];
267 const uint8_t *spy = src[py];
268 const uint8_t *spd = src[pd];
269 uint8_t **dst = out->data;
270 uint8_t *dpx = dst[px];
271 uint8_t *dpy = dst[py];
272 uint8_t *dpd = dst[pd];
278 for (i = 0; i < h; i++) {
279 const int iwx = i * slinesizex;
280 const int iwy = i * slinesizey;
281 for (j = 0; j < w; j++) {
282 const int x = spx[iwx + j];
283 const int y = spy[iwy + j];
284 const int pos = y * dlinesize + x;
286 dpd[pos] = FFMIN(dpd[pos] + intensity, 255);
292 for (i = 0; i < h; i++) {
293 const int iwx = i * slinesizex;
294 const int iwy = i * slinesizey;
295 for (j = 0; j < w; j++) {
296 const int x = spx[iwx + j];
297 const int y = spy[iwy + j];
298 const int pos = y * dlinesize + x;
300 dst[0][pos] = FFMIN(dst[0][pos] + intensity, 255);
301 dst[1][pos] = FFMIN(dst[1][pos] + intensity, 255);
302 dst[2][pos] = FFMIN(dst[2][pos] + intensity, 255);
308 if (s->mode == COLOR) {
309 for (i = 0; i < out->height; i++) {
310 for (j = 0; j < out->width; j++) {
311 if (!dpd[i * out->linesize[pd] + j]) {
312 dpx[i * out->linesize[px] + j] = j;
313 dpy[i * out->linesize[py] + j] = i;
321 for (i = 0; i < h; i++) {
322 const int iw1 = i * slinesizex;
323 const int iw2 = i * slinesizey;
324 for (j = 0; j < w; j++) {
325 const int x = spx[iw1 + j];
326 const int y = spy[iw2 + j];
327 const int pos = y * dlinesize + x;
330 dpd[pos] = FFABS(128 - x) + FFABS(128 - y);
338 for (i = 0; i < h; i++) {
339 const int iw1 = i * slinesizex;
340 const int iw2 = i * slinesizey;
341 for (j = 0; j < w; j++) {
342 const int x = spx[iw1 + j];
343 const int y = spy[iw2 + j];
344 const int pos = y * dlinesize + x;
347 dpd[pos] = FFMIN(x + y, 255);
357 for (i = 0; i < h; i++) {
358 const int iw1 = i * slinesizex;
359 const int iw2 = i * slinesizey;
360 for (j = 0; j < w; j++) {
361 const int x = spx[iw1 + j];
362 const int y = spy[iw2 + j];
363 const int pos = y * dlinesize + x;
365 dpd[pos] = FFMIN(255, dpd[pos] + intensity);
374 for (i = 0; i < h; i++) {
375 const int iwx = i * slinesizex;
376 const int iwy = i * slinesizey;
377 const int iwd = i * slinesized;
378 for (j = 0; j < w; j++) {
379 const int x = spx[iwx + j];
380 const int y = spy[iwy + j];
381 const int pos = y * dlinesize + x;
383 dpd[pos] = FFMAX(spd[iwd + j], dpd[pos]);
398 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
400 AVFilterContext *ctx = inlink->dst;
401 VectorscopeContext *s = ctx->priv;
402 AVFilterLink *outlink = ctx->outputs[0];
407 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
410 return AVERROR(ENOMEM);
415 for (k = 0; k < 4 && dst[k]; k++)
416 for (i = 0; i < outlink->h ; i++)
417 memset(dst[k] + i * out->linesize[k],
418 s->mode == COLOR && k == s->pd ? 0 : s->bg_color[k], outlink->w);
420 vectorscope(s, in, out, s->pd);
423 return ff_filter_frame(outlink, out);
426 static const AVFilterPad inputs[] = {
429 .type = AVMEDIA_TYPE_VIDEO,
430 .filter_frame = filter_frame,
431 .config_props = config_input,
436 static const AVFilterPad outputs[] = {
439 .type = AVMEDIA_TYPE_VIDEO,
440 .config_props = config_output,
445 AVFilter ff_vf_vectorscope = {
446 .name = "vectorscope",
447 .description = NULL_IF_CONFIG_SMALL("Video vectorscope."),
448 .priv_size = sizeof(VectorscopeContext),
449 .priv_class = &vectorscope_class,
450 .query_formats = query_formats,