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/intreadwrite.h"
23 #include "libavutil/opt.h"
24 #include "libavutil/parseutils.h"
25 #include "libavutil/pixdesc.h"
31 enum VectorscopeMode {
40 typedef struct VectorscopeContext {
45 const uint8_t *bg_color;
54 uint8_t peak[1024][1024];
56 void (*vectorscope)(struct VectorscopeContext *s,
57 AVFrame *in, AVFrame *out, int pd);
60 #define OFFSET(x) offsetof(VectorscopeContext, x)
61 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
63 static const AVOption vectorscope_options[] = {
64 { "mode", "set vectorscope mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, MODE_NB-1, FLAGS, "mode"},
65 { "m", "set vectorscope mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, MODE_NB-1, FLAGS, "mode"},
66 { "gray", 0, 0, AV_OPT_TYPE_CONST, {.i64=GRAY}, 0, 0, FLAGS, "mode" },
67 { "color", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR}, 0, 0, FLAGS, "mode" },
68 { "color2", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR2}, 0, 0, FLAGS, "mode" },
69 { "color3", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR3}, 0, 0, FLAGS, "mode" },
70 { "color4", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR4}, 0, 0, FLAGS, "mode" },
71 { "x", "set color component on X axis", OFFSET(x), AV_OPT_TYPE_INT, {.i64=1}, 0, 2, FLAGS},
72 { "y", "set color component on Y axis", OFFSET(y), AV_OPT_TYPE_INT, {.i64=2}, 0, 2, FLAGS},
73 { "intensity", "set intensity", OFFSET(fintensity), AV_OPT_TYPE_FLOAT, {.dbl=0.004}, 0, 1, FLAGS},
74 { "i", "set intensity", OFFSET(fintensity), AV_OPT_TYPE_FLOAT, {.dbl=0.004}, 0, 1, FLAGS},
75 { "envelope", "set envelope", OFFSET(envelope), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, FLAGS, "envelope"},
76 { "e", "set envelope", OFFSET(envelope), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, FLAGS, "envelope"},
77 { "none", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "envelope" },
78 { "instant", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "envelope" },
79 { "peak", 0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "envelope" },
80 { "peak+instant", 0, 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, FLAGS, "envelope" },
84 AVFILTER_DEFINE_CLASS(vectorscope);
86 static const enum AVPixelFormat out_yuv8_pix_fmts[] = {
87 AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P,
91 static const enum AVPixelFormat out_yuv9_pix_fmts[] = {
96 static const enum AVPixelFormat out_yuv10_pix_fmts[] = {
101 static const enum AVPixelFormat out_rgb8_pix_fmts[] = {
102 AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP,
106 static const enum AVPixelFormat out_rgb9_pix_fmts[] = {
111 static const enum AVPixelFormat out_rgb10_pix_fmts[] = {
116 static const enum AVPixelFormat in1_pix_fmts[] = {
117 AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P,
118 AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUV444P10,
119 AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP,
120 AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10,
124 static const enum AVPixelFormat in2_pix_fmts[] = {
125 AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVJ420P,
126 AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVJ422P,
127 AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P,
128 AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUVJ411P,
129 AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV410P,
130 AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP,
131 AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10,
132 AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
133 AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10,
137 static int query_formats(AVFilterContext *ctx)
139 VectorscopeContext *s = ctx->priv;
140 const enum AVPixelFormat *out_pix_fmts;
141 const AVPixFmtDescriptor *desc;
142 AVFilterFormats *avff;
143 int depth, rgb, i, ret;
145 if (!ctx->inputs[0]->in_formats ||
146 !ctx->inputs[0]->in_formats->nb_formats) {
147 return AVERROR(EAGAIN);
150 if (!ctx->inputs[0]->out_formats) {
151 const enum AVPixelFormat *in_pix_fmts;
153 if ((s->x == 1 && s->y == 2) || (s->x == 2 && s->y == 1))
154 in_pix_fmts = in2_pix_fmts;
156 in_pix_fmts = in1_pix_fmts;
157 if ((ret = ff_formats_ref(ff_make_format_list(in_pix_fmts), &ctx->inputs[0]->out_formats)) < 0)
161 avff = ctx->inputs[0]->in_formats;
162 desc = av_pix_fmt_desc_get(avff->formats[0]);
163 rgb = desc->flags & AV_PIX_FMT_FLAG_RGB;
164 depth = desc->comp[0].depth;
165 for (i = 1; i < avff->nb_formats; i++) {
166 desc = av_pix_fmt_desc_get(avff->formats[i]);
167 if (rgb != (desc->flags & AV_PIX_FMT_FLAG_RGB) ||
168 depth != desc->comp[0].depth)
169 return AVERROR(EAGAIN);
172 if (rgb && depth == 8)
173 out_pix_fmts = out_rgb8_pix_fmts;
174 else if (rgb && depth == 9)
175 out_pix_fmts = out_rgb9_pix_fmts;
176 else if (rgb && depth == 10)
177 out_pix_fmts = out_rgb10_pix_fmts;
179 out_pix_fmts = out_yuv9_pix_fmts;
180 else if (depth == 10)
181 out_pix_fmts = out_yuv10_pix_fmts;
183 out_pix_fmts = out_yuv8_pix_fmts;
184 if ((ret = ff_formats_ref(ff_make_format_list(out_pix_fmts), &ctx->outputs[0]->in_formats)) < 0)
190 static const uint8_t black_yuva_color[4] = { 0, 127, 127, 0 };
191 static const uint8_t black_gbrp_color[4] = { 0, 0, 0, 0 };
193 static int config_output(AVFilterLink *outlink)
195 VectorscopeContext *s = outlink->src->priv;
197 s->intensity = s->fintensity * (s->size - 1);
198 outlink->h = outlink->w = s->size;
199 outlink->sample_aspect_ratio = (AVRational){1,1};
203 static void envelope_instant16(VectorscopeContext *s, AVFrame *out)
205 const int dlinesize = out->linesize[0] / 2;
206 uint16_t *dpd = s->mode == COLOR || !s->is_yuv ? (uint16_t *)out->data[s->pd] : (uint16_t *)out->data[0];
207 const int max = s->size - 1;
210 for (i = 0; i < out->height; i++) {
211 for (j = 0; j < out->width; j++) {
212 const int pos = i * dlinesize + j;
213 const int poa = (i - 1) * dlinesize + j;
214 const int pob = (i + 1) * dlinesize + j;
216 if (dpd[pos] && (((!j || !dpd[pos - 1]) || ((j == (out->width - 1)) || !dpd[pos + 1]))
217 || ((!i || !dpd[poa]) || ((i == (out->height - 1)) || !dpd[pob])))) {
224 static void envelope_peak16(VectorscopeContext *s, AVFrame *out)
226 const int dlinesize = out->linesize[0] / 2;
227 uint16_t *dpd = s->mode == COLOR || !s->is_yuv ? (uint16_t *)out->data[s->pd] : (uint16_t *)out->data[0];
228 const int max = s->size - 1;
231 for (i = 0; i < out->height; i++) {
232 for (j = 0; j < out->width; j++) {
233 const int pos = i * dlinesize + j;
240 if (s->envelope == 3)
241 envelope_instant16(s, out);
243 for (i = 0; i < out->height; i++) {
244 for (j = 0; j < out->width; j++) {
245 const int pos = i * dlinesize + j;
247 if (s->peak[i][j] && (((!j || !s->peak[i][j-1]) || ((j == (out->width - 1)) || !s->peak[i][j + 1]))
248 || ((!i || !s->peak[i-1][j]) || ((i == (out->height - 1)) || !s->peak[i + 1][j])))) {
255 static void envelope_instant(VectorscopeContext *s, AVFrame *out)
257 const int dlinesize = out->linesize[0];
258 uint8_t *dpd = s->mode == COLOR || !s->is_yuv ? out->data[s->pd] : out->data[0];
261 for (i = 0; i < out->height; i++) {
262 for (j = 0; j < out->width; j++) {
263 const int pos = i * dlinesize + j;
264 const int poa = (i - 1) * dlinesize + j;
265 const int pob = (i + 1) * dlinesize + j;
267 if (dpd[pos] && (((!j || !dpd[pos - 1]) || ((j == (out->width - 1)) || !dpd[pos + 1]))
268 || ((!i || !dpd[poa]) || ((i == (out->height - 1)) || !dpd[pob])))) {
275 static void envelope_peak(VectorscopeContext *s, AVFrame *out)
277 const int dlinesize = out->linesize[0];
278 uint8_t *dpd = s->mode == COLOR || !s->is_yuv ? out->data[s->pd] : out->data[0];
281 for (i = 0; i < out->height; i++) {
282 for (j = 0; j < out->width; j++) {
283 const int pos = i * dlinesize + j;
290 if (s->envelope == 3)
291 envelope_instant(s, out);
293 for (i = 0; i < out->height; i++) {
294 for (j = 0; j < out->width; j++) {
295 const int pos = i * dlinesize + j;
297 if (s->peak[i][j] && (((!j || !s->peak[i][j-1]) || ((j == (out->width - 1)) || !s->peak[i][j + 1]))
298 || ((!i || !s->peak[i-1][j]) || ((i == (out->height - 1)) || !s->peak[i + 1][j])))) {
305 static void envelope16(VectorscopeContext *s, AVFrame *out)
309 } else if (s->envelope == 1) {
310 envelope_instant16(s, out);
312 envelope_peak16(s, out);
316 static void envelope(VectorscopeContext *s, AVFrame *out)
320 } else if (s->envelope == 1) {
321 envelope_instant(s, out);
323 envelope_peak(s, out);
327 static void vectorscope16(VectorscopeContext *s, AVFrame *in, AVFrame *out, int pd)
329 const uint16_t * const *src = (const uint16_t * const *)in->data;
330 const int slinesizex = in->linesize[s->x] / 2;
331 const int slinesizey = in->linesize[s->y] / 2;
332 const int slinesized = in->linesize[pd] / 2;
333 const int dlinesize = out->linesize[0] / 2;
334 const int intensity = s->intensity;
335 const int px = s->x, py = s->y;
336 const int h = s->planeheight[py];
337 const int w = s->planewidth[px];
338 const uint16_t *spx = src[px];
339 const uint16_t *spy = src[py];
340 const uint16_t *spd = src[pd];
341 const int hsub = s->hsub;
342 const int vsub = s->vsub;
343 uint16_t **dst = (uint16_t **)out->data;
344 uint16_t *dpx = dst[px];
345 uint16_t *dpy = dst[py];
346 uint16_t *dpd = dst[pd];
347 const int max = s->size - 1;
348 const int mid = s->size / 2;
351 for (k = 0; k < 4 && dst[k]; k++) {
352 const int mult = s->mult;
354 for (i = 0; i < out->height ; i++)
355 for (j = 0; j < out->width; j++)
356 AV_WN16(out->data[k] + i * out->linesize[k] + j * 2,
357 s->mode == COLOR && k == s->pd ? 0 : s->bg_color[k] * mult);
364 for (i = 0; i < h; i++) {
365 const int iwx = i * slinesizex;
366 const int iwy = i * slinesizey;
367 for (j = 0; j < w; j++) {
368 const int x = FFMIN(spx[iwx + j], max);
369 const int y = FFMIN(spy[iwy + j], max);
370 const int pos = y * dlinesize + x;
372 dpd[pos] = FFMIN(dpd[pos] + intensity, max);
378 for (i = 0; i < h; i++) {
379 const int iwx = i * slinesizex;
380 const int iwy = i * slinesizey;
381 for (j = 0; j < w; j++) {
382 const int x = FFMIN(spx[iwx + j], max);
383 const int y = FFMIN(spy[iwy + j], max);
384 const int pos = y * dlinesize + x;
386 dst[0][pos] = FFMIN(dst[0][pos] + intensity, max);
387 dst[1][pos] = FFMIN(dst[1][pos] + intensity, max);
388 dst[2][pos] = FFMIN(dst[2][pos] + intensity, max);
397 for (i = 0; i < h; i++) {
398 const int iw1 = i * slinesizex;
399 const int iw2 = i * slinesizey;
400 for (j = 0; j < w; j++) {
401 const int x = FFMIN(spx[iw1 + j], max);
402 const int y = FFMIN(spy[iw2 + j], max);
403 const int pos = y * dlinesize + x;
406 dpd[pos] = FFABS(mid - x) + FFABS(mid - y);
414 for (i = 0; i < h; i++) {
415 const int iw1 = i * slinesizex;
416 const int iw2 = i * slinesizey;
417 for (j = 0; j < w; j++) {
418 const int x = FFMIN(spx[iw1 + j], max);
419 const int y = FFMIN(spy[iw2 + j], max);
420 const int pos = y * dlinesize + x;
423 dpd[pos] = FFMIN(x + y, max);
433 for (i = 0; i < h; i++) {
434 const int iw1 = i * slinesizex;
435 const int iw2 = i * slinesizey;
436 for (j = 0; j < w; j++) {
437 const int x = FFMIN(spx[iw1 + j], max);
438 const int y = FFMIN(spy[iw2 + j], max);
439 const int pos = y * dlinesize + x;
441 dpd[pos] = FFMIN(max, dpd[pos] + intensity);
450 for (i = 0; i < in->height; i++) {
451 const int iwx = (i >> vsub) * slinesizex;
452 const int iwy = (i >> vsub) * slinesizey;
453 const int iwd = i * slinesized;
454 for (j = 0; j < in->width; j++) {
455 const int x = FFMIN(spx[iwx + (j >> hsub)], max);
456 const int y = FFMIN(spy[iwy + (j >> hsub)], max);
457 const int pos = y * dlinesize + x;
459 dpd[pos] = FFMAX(spd[iwd + j], dpd[pos]);
473 if (s->mode == COLOR) {
474 for (i = 0; i < out->height; i++) {
475 for (j = 0; j < out->width; j++) {
476 if (!dpd[i * dlinesize + j]) {
477 dpx[i * dlinesize + j] = j;
478 dpy[i * dlinesize + j] = i;
479 dpd[i * dlinesize + j] = mid;
486 static void vectorscope8(VectorscopeContext *s, AVFrame *in, AVFrame *out, int pd)
488 const uint8_t * const *src = (const uint8_t * const *)in->data;
489 const int slinesizex = in->linesize[s->x];
490 const int slinesizey = in->linesize[s->y];
491 const int slinesized = in->linesize[pd];
492 const int dlinesize = out->linesize[0];
493 const int intensity = s->intensity;
494 const int px = s->x, py = s->y;
495 const int h = s->planeheight[py];
496 const int w = s->planewidth[px];
497 const uint8_t *spx = src[px];
498 const uint8_t *spy = src[py];
499 const uint8_t *spd = src[pd];
500 const int hsub = s->hsub;
501 const int vsub = s->vsub;
502 uint8_t **dst = out->data;
503 uint8_t *dpx = dst[px];
504 uint8_t *dpy = dst[py];
505 uint8_t *dpd = dst[pd];
508 for (k = 0; k < 4 && dst[k]; k++)
509 for (i = 0; i < out->height ; i++)
510 memset(dst[k] + i * out->linesize[k],
511 s->mode == COLOR && k == s->pd ? 0 : s->bg_color[k], out->width);
517 for (i = 0; i < h; i++) {
518 const int iwx = i * slinesizex;
519 const int iwy = i * slinesizey;
520 for (j = 0; j < w; j++) {
521 const int x = spx[iwx + j];
522 const int y = spy[iwy + j];
523 const int pos = y * dlinesize + x;
525 dpd[pos] = FFMIN(dpd[pos] + intensity, 255);
531 for (i = 0; i < h; i++) {
532 const int iwx = i * slinesizex;
533 const int iwy = i * slinesizey;
534 for (j = 0; j < w; j++) {
535 const int x = spx[iwx + j];
536 const int y = spy[iwy + j];
537 const int pos = y * dlinesize + x;
539 dst[0][pos] = FFMIN(dst[0][pos] + intensity, 255);
540 dst[1][pos] = FFMIN(dst[1][pos] + intensity, 255);
541 dst[2][pos] = FFMIN(dst[2][pos] + intensity, 255);
550 for (i = 0; i < h; i++) {
551 const int iw1 = i * slinesizex;
552 const int iw2 = i * slinesizey;
553 for (j = 0; j < w; j++) {
554 const int x = spx[iw1 + j];
555 const int y = spy[iw2 + j];
556 const int pos = y * dlinesize + x;
559 dpd[pos] = FFABS(128 - x) + FFABS(128 - y);
567 for (i = 0; i < h; i++) {
568 const int iw1 = i * slinesizex;
569 const int iw2 = i * slinesizey;
570 for (j = 0; j < w; j++) {
571 const int x = spx[iw1 + j];
572 const int y = spy[iw2 + j];
573 const int pos = y * dlinesize + x;
576 dpd[pos] = FFMIN(x + y, 255);
586 for (i = 0; i < h; i++) {
587 const int iw1 = i * slinesizex;
588 const int iw2 = i * slinesizey;
589 for (j = 0; j < w; j++) {
590 const int x = spx[iw1 + j];
591 const int y = spy[iw2 + j];
592 const int pos = y * dlinesize + x;
594 dpd[pos] = FFMIN(255, dpd[pos] + intensity);
603 for (i = 0; i < in->height; i++) {
604 const int iwx = (i >> vsub) * slinesizex;
605 const int iwy = (i >> vsub) * slinesizey;
606 const int iwd = i * slinesized;
607 for (j = 0; j < in->width; j++) {
608 const int x = spx[iwx + (j >> hsub)];
609 const int y = spy[iwy + (j >> hsub)];
610 const int pos = y * dlinesize + x;
612 dpd[pos] = FFMAX(spd[iwd + j], dpd[pos]);
626 if (s->mode == COLOR) {
627 for (i = 0; i < out->height; i++) {
628 for (j = 0; j < out->width; j++) {
629 if (!dpd[i * out->linesize[pd] + j]) {
630 dpx[i * out->linesize[px] + j] = j;
631 dpy[i * out->linesize[py] + j] = i;
632 dpd[i * out->linesize[pd] + j] = 128;
639 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
641 AVFilterContext *ctx = inlink->dst;
642 VectorscopeContext *s = ctx->priv;
643 AVFilterLink *outlink = ctx->outputs[0];
646 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
649 return AVERROR(ENOMEM);
653 s->vectorscope(s, in, out, s->pd);
656 return ff_filter_frame(outlink, out);
659 static int config_input(AVFilterLink *inlink)
661 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
662 VectorscopeContext *s = inlink->dst->priv;
664 s->is_yuv = !(desc->flags & AV_PIX_FMT_FLAG_RGB);
665 s->size = 1 << desc->comp[0].depth;
666 s->mult = s->size / 256;
668 if (s->mode == GRAY && s->is_yuv)
671 if ((s->x == 1 && s->y == 2) || (s->x == 2 && s->y == 1))
673 else if ((s->x == 0 && s->y == 2) || (s->x == 2 && s->y == 0))
675 else if ((s->x == 0 && s->y == 1) || (s->x == 1 && s->y == 0))
680 s->vectorscope = vectorscope8;
682 s->vectorscope = vectorscope16;
684 switch (inlink->format) {
685 case AV_PIX_FMT_GBRP10:
686 case AV_PIX_FMT_GBRP9:
687 case AV_PIX_FMT_GBRAP:
688 case AV_PIX_FMT_GBRP:
689 s->bg_color = black_gbrp_color;
692 s->bg_color = black_yuva_color;
695 s->hsub = desc->log2_chroma_w;
696 s->vsub = desc->log2_chroma_h;
697 s->planeheight[1] = s->planeheight[2] = FF_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
698 s->planeheight[0] = s->planeheight[3] = inlink->h;
699 s->planewidth[1] = s->planewidth[2] = FF_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
700 s->planewidth[0] = s->planewidth[3] = inlink->w;
705 static const AVFilterPad inputs[] = {
708 .type = AVMEDIA_TYPE_VIDEO,
709 .filter_frame = filter_frame,
710 .config_props = config_input,
715 static const AVFilterPad outputs[] = {
718 .type = AVMEDIA_TYPE_VIDEO,
719 .config_props = config_output,
724 AVFilter ff_vf_vectorscope = {
725 .name = "vectorscope",
726 .description = NULL_IF_CONFIG_SMALL("Video vectorscope."),
727 .priv_size = sizeof(VectorscopeContext),
728 .priv_class = &vectorscope_class,
729 .query_formats = query_formats,