Message ID | 20170425221522.13192-1-onemda@gmail.com |
---|---|
State | Superseded |
Headers | show |
> On Apr 25, 2017, at 6:15 PM, Paul B Mahol <onemda@gmail.com> wrote: > > Signed-off-by: Paul B Mahol <onemda@gmail.com> > --- > libavfilter/Makefile | 1 + > libavfilter/allfilters.c | 1 + > libavfilter/vf_datascope.c | 299 +++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 301 insertions(+) > > diff --git a/libavfilter/Makefile b/libavfilter/Makefile > index e40c6fe..66c36e4 100644 > --- a/libavfilter/Makefile > +++ b/libavfilter/Makefile > @@ -236,6 +236,7 @@ OBJS-$(CONFIG_NULL_FILTER) += vf_null.o > OBJS-$(CONFIG_OCR_FILTER) += vf_ocr.o > OBJS-$(CONFIG_OCV_FILTER) += vf_libopencv.o > OBJS-$(CONFIG_OPENCL) += deshake_opencl.o unsharp_opencl.o > +OBJS-$(CONFIG_OSCILLOSCOPE_FILTER) += vf_datascope.o > OBJS-$(CONFIG_OVERLAY_FILTER) += vf_overlay.o dualinput.o framesync.o > OBJS-$(CONFIG_OWDENOISE_FILTER) += vf_owdenoise.o > OBJS-$(CONFIG_PAD_FILTER) += vf_pad.o > diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c > index 0852b54..8fb87eb 100644 > --- a/libavfilter/allfilters.c > +++ b/libavfilter/allfilters.c > @@ -246,6 +246,7 @@ static void register_all(void) > REGISTER_FILTER(NULL, null, vf); > REGISTER_FILTER(OCR, ocr, vf); > REGISTER_FILTER(OCV, ocv, vf); > + REGISTER_FILTER(OSCILLOSCOPE, oscilloscope, vf); > REGISTER_FILTER(OVERLAY, overlay, vf); > REGISTER_FILTER(OWDENOISE, owdenoise, vf); > REGISTER_FILTER(PAD, pad, vf); > diff --git a/libavfilter/vf_datascope.c b/libavfilter/vf_datascope.c > index 5ad4bb8..080a97d 100644 > --- a/libavfilter/vf_datascope.c > +++ b/libavfilter/vf_datascope.c > @@ -630,3 +630,302 @@ AVFilter ff_vf_pixscope = { > .inputs = pixscope_inputs, > .outputs = pixscope_outputs, > }; > + > +typedef struct PixelValues { > + uint16_t p[4]; > +} PixelValues; > + > +typedef struct OscilloscopeContext { > + const AVClass *class; > + > + float xpos, ypos; > + float ty; > + float size; > + float tilt; > + float tsize; > + float o; > + int components; > + int grid; > + > + int x1, y1, x2, y2; > + int ox, oy; > + int width; > + > + int nb_planes; > + int nb_comps; > + int is_rgb; > + uint8_t rgba_map[4]; > + FFDrawContext draw; > + FFDrawColor dark; > + FFDrawColor black; > + FFDrawColor white; > + FFDrawColor green; > + FFDrawColor blue; > + FFDrawColor red; > + FFDrawColor gray; > + FFDrawColor *colors[4]; > + > + int nb_values; > + PixelValues *values; > + > + void (*pick_color)(FFDrawContext *draw, FFDrawColor *color, AVFrame *in, int x, int y, int *value); > +} OscilloscopeContext; > + > +#define OOFFSET(x) offsetof(OscilloscopeContext, x) > + > +static const AVOption oscilloscope_options[] = { > + { "x", "set scope x position", OOFFSET(xpos), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGS }, > + { "y", "set scope y position", OOFFSET(ypos), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGS }, I’m curious why floats are used for x,y coordinates within a frame rather than integers. > + { "s", "set scope size", OOFFSET(size), AV_OPT_TYPE_FLOAT, {.dbl=0.8}, 0, 1, FLAGS }, > + { "t", "set scope tilt", OOFFSET(tilt), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGS }, > + { "o", "set opacity", OOFFSET(o), AV_OPT_TYPE_FLOAT, {.dbl=0.8}, 0, 1, FLAGS }, > + { "ty", "set trace y position", OOFFSET(ty), AV_OPT_TYPE_FLOAT, {.dbl=0.9}, 0, 1, FLAGS }, > + { "ts", "set trace size", OOFFSET(tsize), AV_OPT_TYPE_FLOAT, {.dbl=0.8},.1, 1, FLAGS }, > + { "c", "set components to trace", OOFFSET(components), AV_OPT_TYPE_INT, {.i64=7}, 0, 15, FLAGS }, > + { "g", "draw trace grid", OOFFSET(grid), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, > + { NULL } > +}; > + > +AVFILTER_DEFINE_CLASS(oscilloscope); > + > +static void oscilloscope_uninit(AVFilterContext *ctx) > +{ > + OscilloscopeContext *s = ctx->priv; > + > + av_freep(&s->values); > +} > + > +static int oscilloscope_config_input(AVFilterLink *inlink) > +{ > + OscilloscopeContext *s = inlink->dst->priv; > + int cx, cy, size; > + float tilt; > + > + s->nb_planes = av_pix_fmt_count_planes(inlink->format); > + ff_draw_init(&s->draw, inlink->format, 0); > + ff_draw_color(&s->draw, &s->dark, (uint8_t[]){ 0, 0, 0, s->o * 255} ); > + ff_draw_color(&s->draw, &s->black, (uint8_t[]){ 0, 0, 0, 255} ); > + ff_draw_color(&s->draw, &s->white, (uint8_t[]){ 255, 255, 255, 255} ); > + ff_draw_color(&s->draw, &s->green, (uint8_t[]){ 0, 255, 0, 255} ); > + ff_draw_color(&s->draw, &s->blue, (uint8_t[]){ 0, 0, 255, 255} ); > + ff_draw_color(&s->draw, &s->red, (uint8_t[]){ 255, 0, 0, 255} ); > + ff_draw_color(&s->draw, &s->gray, (uint8_t[]){ 128, 128, 128, 255} ); > + s->nb_comps = s->draw.desc->nb_components; > + s->is_rgb = s->draw.desc->flags & AV_PIX_FMT_FLAG_RGB; > + > + if (s->is_rgb) { > + s->colors[0] = &s->red; > + s->colors[1] = &s->green; > + s->colors[2] = &s->blue; > + s->colors[3] = &s->white; > + ff_fill_rgba_map(s->rgba_map, inlink->format); > + } else { > + s->colors[0] = &s->white; > + s->colors[1] = &s->blue; > + s->colors[2] = &s->red; > + s->colors[3] = &s->white; > + s->rgba_map[0] = 0; > + s->rgba_map[1] = 1; > + s->rgba_map[2] = 2; > + s->rgba_map[3] = 3; > + } > + > + if (s->draw.desc->comp[0].depth <= 8) { > + s->pick_color = pick_color8; > + } else { > + s->pick_color = pick_color16; > + } > + > + if (inlink->h < 300) { > + av_log(inlink->dst, AV_LOG_ERROR, "min supported height is 300\n"); > + return AVERROR(EINVAL); > + } > + > + cx = s->xpos * inlink->w; > + cy = s->ypos * inlink->h; > + size = s->size * hypot(inlink->w, inlink->h); > + s->width = s->tsize * (inlink->w); > + > + s->values = av_calloc(size, sizeof(*s->values)); > + if (!s->values) > + return AVERROR(ENOMEM); > + > + tilt = (s->tilt - 0.5) * M_PI; > + s->x1 = cx - size / 2.0 * cosf(tilt); > + s->x2 = cx + size / 2.0 * cosf(tilt); > + s->y1 = cy - size / 2.0 * sinf(tilt); > + s->y2 = cy + size / 2.0 * sinf(tilt); > + s->ox = (inlink->w - s->width) >> 1; > + s->oy = (inlink->h - 300) * s->ty; > + > + return 0; > +} > + > +static void draw_scope(OscilloscopeContext *s, int x0, int y0, int x1, int y1, > + AVFrame *out, PixelValues *p) > +{ > + int dx = FFABS(x1 - x0), sx = x0 < x1 ? 1 : -1; > + int dy = FFABS(y1 - y0), sy = y0 < y1 ? 1 : -1; > + int err = (dx > dy ? dx : -dy) / 2, e2; > + > + for (;;) { > + if (x0 > 0 && y0 > 0 && x0 < out->width && y0 < out->height) { > + FFDrawColor color = { { 0 } }; > + int value[4] = { 0 }; > + > + s->pick_color(&s->draw, &color, out, x0, y0, value); > + s->values[s->nb_values].p[0] = value[0]; > + s->values[s->nb_values].p[1] = value[1]; > + s->values[s->nb_values].p[2] = value[2]; > + s->values[s->nb_values].p[3] = value[3]; > + s->nb_values++; > + > + if (s->draw.nb_planes == 1) { > + int i; > + > + for (i = 0; i < s->draw.pixelstep[0]; i++) > + out->data[0][out->linesize[0] * y0 + x0 * s->draw.pixelstep[0] + i] = 255; > + } else { > + out->data[0][out->linesize[0] * y0 + x0] = 255; > + } > + } > + > + if (x0 == x1 && y0 == y1) > + break; > + > + e2 = err; > + > + if (e2 >-dx) { > + err -= dy; > + x0 += sx; > + } > + > + if (e2 < dy) { > + err += dx; > + y0 += sy; > + } > + } > +} > + > +static void draw_line(FFDrawContext *draw, int x0, int y0, int x1, int y1, > + AVFrame *out, FFDrawColor *color) > +{ > + int dx = FFABS(x1 - x0), sx = x0 < x1 ? 1 : -1; > + int dy = FFABS(y1 - y0), sy = y0 < y1 ? 1 : -1; > + int err = (dx > dy ? dx : -dy) / 2, e2; > + int p, i; > + > + for (;;) { > + for (p = 0; p < draw->nb_planes; p++) { > + if (draw->nb_planes == 1) { > + for (i = 0; i < 4; i++) { > + out->data[0][y0 * out->linesize[0] + x0 * draw->pixelstep[0] + i] = color->comp[0].u8[i]; > + } > + } else { > + out->data[p][out->linesize[p] * (y0 >> draw->vsub[p]) + (x0 >> draw->hsub[p])] = color->comp[p].u8[0]; > + } > + } > + > + if (x0 == x1 && y0 == y1) > + break; > + > + e2 = err; > + > + if (e2 >-dx) { > + err -= dy; > + x0 += sx; > + } > + > + if (e2 < dy) { > + err += dx; > + y0 += sy; > + } > + } > +} > + > +static int oscilloscope_filter_frame(AVFilterLink *inlink, AVFrame *frame) > +{ > + AVFilterContext *ctx = inlink->dst; > + OscilloscopeContext *s = ctx->priv; > + AVFilterLink *outlink = ctx->outputs[0]; > + float average[4] = { 0 }; > + int max[4] = { 0 }; > + int min[4] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX }; > + int i, c; > + > + s->nb_values = 0; > + draw_scope(s, s->x1, s->y1, s->x2, s->y2, frame, s->values); > + ff_blend_rectangle(&s->draw, &s->dark, frame->data, frame->linesize, > + frame->width, frame->height, > + s->ox, s->oy, s->width, 300); It appears that the plotted scope is padded to 300, though the trace usually is 256 (2^8) lines tall. Are the other 44 lines intended to store something? > + if (s->grid) { > + ff_fill_rectangle(&s->draw, &s->gray, frame->data, frame->linesize, > + s->ox, s->oy, s->width - 1, 1); > + > + ff_fill_rectangle(&s->draw, &s->gray, frame->data, frame->linesize, > + s->ox, s->oy + 64, s->width, 1); > + > + ff_fill_rectangle(&s->draw, &s->gray, frame->data, frame->linesize, > + s->ox, s->oy + 128, s->width, 1); > + > + ff_fill_rectangle(&s->draw, &s->gray, frame->data, frame->linesize, > + s->ox, s->oy + 192, s->width, 1); > + > + ff_fill_rectangle(&s->draw, &s->gray, frame->data, frame->linesize, > + s->ox, s->oy + 256, s->width, 1); > + > + for (i = 0; i < 10; i++) { > + ff_fill_rectangle(&s->draw, &s->gray, frame->data, frame->linesize, > + s->ox + i * (s->width - 1) / 10, s->oy, 1, 256); > + } > + > + ff_fill_rectangle(&s->draw, &s->gray, frame->data, frame->linesize, > + s->ox + s->width - 1, s->oy, 1, 256); > + } > + > + for (i = 1; i < s->nb_values; i++) { > + for (c = 0; c < s->nb_comps; c++) { > + if ((1 << c) & s->components) { > + int x = i * s->width / s->nb_values; > + int px = (i - 1) * s->width / s->nb_values; > + int py = 255 - s->values[i-1].p[c]; > + int y = 255 - s->values[i].p[c]; > + > + draw_line(&s->draw, s->ox + x, s->oy + y, s->ox + px, s->oy + py, frame, s->colors[c]); > + } > + } > + } > + > + return ff_filter_frame(outlink, frame); > +} > + > +static const AVFilterPad oscilloscope_inputs[] = { > + { > + .name = "default", > + .type = AVMEDIA_TYPE_VIDEO, > + .filter_frame = oscilloscope_filter_frame, > + .config_props = oscilloscope_config_input, > + .needs_writable = 1, > + }, > + { NULL } > +}; > + > +static const AVFilterPad oscilloscope_outputs[] = { > + { > + .name = "default", > + .type = AVMEDIA_TYPE_VIDEO, > + }, > + { NULL } > +}; > + > +AVFilter ff_vf_oscilloscope = { > + .name = "oscilloscope", > + .description = NULL_IF_CONFIG_SMALL("2D Video Oscilloscope."), > + .priv_size = sizeof(OscilloscopeContext), > + .priv_class = &oscilloscope_class, > + .query_formats = query_formats, > + .uninit = oscilloscope_uninit, > + .inputs = oscilloscope_inputs, > + .outputs = oscilloscope_outputs, > +}; > -- > 2.9.3 A crash on >8 bit: ./ffplay -f lavfi -i mandelbrot -vf format=yuv422p10le,oscilloscope ffplay version N-85657-gd2e300a0a0 Copyright (c) 2003-2017 the FFmpeg developers built with Apple LLVM version 8.0.0 (clang-800.0.42.1) configuration: --disable-dwt libavutil 55. 61.100 / 55. 61.100 libavcodec 57. 93.100 / 57. 93.100 libavformat 57. 72.101 / 57. 72.101 libavdevice 57. 7.100 / 57. 7.100 libavfilter 6. 87.100 / 6. 87.100 libswscale 4. 7.101 / 4. 7.101 libswresample 2. 8.100 / 2. 8.100 Input #0, lavfi, from 'mandelbrot':0KB vq= 0KB sq= 0B f=0/0 Duration: N/A, start: 0.000000, bitrate: N/A Stream #0:0: Video: rawvideo (RGB[0] / 0x424752), rgb0, 640x480 [SAR 1:1 DAR 4:3], 25 tbr, 25 tbn, 25 tbc Segmentation fault: 11 Dave Rice
On 4/26/17, Dave Rice <dave@dericed.com> wrote: > > > I'm curious why floats are used for x,y coordinates within a frame rather > than integers. So it the position be set relatively. And for similar results for different resolutions. > > > It appears that the plotted scope is padded to 300, though the trace usually > is 256 (2^8) lines tall. Are the other 44 lines intended to store something? > Yes, some statistics numbers.
> On Apr 26, 2017, at 2:37 AM, Paul B Mahol <onemda@gmail.com> wrote: > > On 4/26/17, Dave Rice <dave@dericed.com> wrote: >> >> >> I'm curious why floats are used for x,y coordinates within a frame rather >> than integers. > > So it the position be set relatively. And for similar results for > different resolutions. Having been used to other filters with integers as x,y, this seems a bit unintuitive to me. In other filters relative positions are set by variables, such as ih/2 as opposed to 0.5. If staying with floats, I'd suggest that the documentation include examples for the use of calling specific lines by integer by including math in the options. Perhaps the statistical area could also state the x,y coords of the two ends of the scope as ints so that a viewer of the image would be clear to what line(s) are included in the analysis. >> It appears that the plotted scope is padded to 300, though the trace usually >> is 256 (2^8) lines tall. Are the other 44 lines intended to store something? >> > > Yes, some statistics numbers. > _______________________________________________ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
diff --git a/libavfilter/Makefile b/libavfilter/Makefile index e40c6fe..66c36e4 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -236,6 +236,7 @@ OBJS-$(CONFIG_NULL_FILTER) += vf_null.o OBJS-$(CONFIG_OCR_FILTER) += vf_ocr.o OBJS-$(CONFIG_OCV_FILTER) += vf_libopencv.o OBJS-$(CONFIG_OPENCL) += deshake_opencl.o unsharp_opencl.o +OBJS-$(CONFIG_OSCILLOSCOPE_FILTER) += vf_datascope.o OBJS-$(CONFIG_OVERLAY_FILTER) += vf_overlay.o dualinput.o framesync.o OBJS-$(CONFIG_OWDENOISE_FILTER) += vf_owdenoise.o OBJS-$(CONFIG_PAD_FILTER) += vf_pad.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 0852b54..8fb87eb 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -246,6 +246,7 @@ static void register_all(void) REGISTER_FILTER(NULL, null, vf); REGISTER_FILTER(OCR, ocr, vf); REGISTER_FILTER(OCV, ocv, vf); + REGISTER_FILTER(OSCILLOSCOPE, oscilloscope, vf); REGISTER_FILTER(OVERLAY, overlay, vf); REGISTER_FILTER(OWDENOISE, owdenoise, vf); REGISTER_FILTER(PAD, pad, vf); diff --git a/libavfilter/vf_datascope.c b/libavfilter/vf_datascope.c index 5ad4bb8..080a97d 100644 --- a/libavfilter/vf_datascope.c +++ b/libavfilter/vf_datascope.c @@ -630,3 +630,302 @@ AVFilter ff_vf_pixscope = { .inputs = pixscope_inputs, .outputs = pixscope_outputs, }; + +typedef struct PixelValues { + uint16_t p[4]; +} PixelValues; + +typedef struct OscilloscopeContext { + const AVClass *class; + + float xpos, ypos; + float ty; + float size; + float tilt; + float tsize; + float o; + int components; + int grid; + + int x1, y1, x2, y2; + int ox, oy; + int width; + + int nb_planes; + int nb_comps; + int is_rgb; + uint8_t rgba_map[4]; + FFDrawContext draw; + FFDrawColor dark; + FFDrawColor black; + FFDrawColor white; + FFDrawColor green; + FFDrawColor blue; + FFDrawColor red; + FFDrawColor gray; + FFDrawColor *colors[4]; + + int nb_values; + PixelValues *values; + + void (*pick_color)(FFDrawContext *draw, FFDrawColor *color, AVFrame *in, int x, int y, int *value); +} OscilloscopeContext; + +#define OOFFSET(x) offsetof(OscilloscopeContext, x) + +static const AVOption oscilloscope_options[] = { + { "x", "set scope x position", OOFFSET(xpos), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGS }, + { "y", "set scope y position", OOFFSET(ypos), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGS }, + { "s", "set scope size", OOFFSET(size), AV_OPT_TYPE_FLOAT, {.dbl=0.8}, 0, 1, FLAGS }, + { "t", "set scope tilt", OOFFSET(tilt), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGS }, + { "o", "set opacity", OOFFSET(o), AV_OPT_TYPE_FLOAT, {.dbl=0.8}, 0, 1, FLAGS }, + { "ty", "set trace y position", OOFFSET(ty), AV_OPT_TYPE_FLOAT, {.dbl=0.9}, 0, 1, FLAGS }, + { "ts", "set trace size", OOFFSET(tsize), AV_OPT_TYPE_FLOAT, {.dbl=0.8},.1, 1, FLAGS }, + { "c", "set components to trace", OOFFSET(components), AV_OPT_TYPE_INT, {.i64=7}, 0, 15, FLAGS }, + { "g", "draw trace grid", OOFFSET(grid), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(oscilloscope); + +static void oscilloscope_uninit(AVFilterContext *ctx) +{ + OscilloscopeContext *s = ctx->priv; + + av_freep(&s->values); +} + +static int oscilloscope_config_input(AVFilterLink *inlink) +{ + OscilloscopeContext *s = inlink->dst->priv; + int cx, cy, size; + float tilt; + + s->nb_planes = av_pix_fmt_count_planes(inlink->format); + ff_draw_init(&s->draw, inlink->format, 0); + ff_draw_color(&s->draw, &s->dark, (uint8_t[]){ 0, 0, 0, s->o * 255} ); + ff_draw_color(&s->draw, &s->black, (uint8_t[]){ 0, 0, 0, 255} ); + ff_draw_color(&s->draw, &s->white, (uint8_t[]){ 255, 255, 255, 255} ); + ff_draw_color(&s->draw, &s->green, (uint8_t[]){ 0, 255, 0, 255} ); + ff_draw_color(&s->draw, &s->blue, (uint8_t[]){ 0, 0, 255, 255} ); + ff_draw_color(&s->draw, &s->red, (uint8_t[]){ 255, 0, 0, 255} ); + ff_draw_color(&s->draw, &s->gray, (uint8_t[]){ 128, 128, 128, 255} ); + s->nb_comps = s->draw.desc->nb_components; + s->is_rgb = s->draw.desc->flags & AV_PIX_FMT_FLAG_RGB; + + if (s->is_rgb) { + s->colors[0] = &s->red; + s->colors[1] = &s->green; + s->colors[2] = &s->blue; + s->colors[3] = &s->white; + ff_fill_rgba_map(s->rgba_map, inlink->format); + } else { + s->colors[0] = &s->white; + s->colors[1] = &s->blue; + s->colors[2] = &s->red; + s->colors[3] = &s->white; + s->rgba_map[0] = 0; + s->rgba_map[1] = 1; + s->rgba_map[2] = 2; + s->rgba_map[3] = 3; + } + + if (s->draw.desc->comp[0].depth <= 8) { + s->pick_color = pick_color8; + } else { + s->pick_color = pick_color16; + } + + if (inlink->h < 300) { + av_log(inlink->dst, AV_LOG_ERROR, "min supported height is 300\n"); + return AVERROR(EINVAL); + } + + cx = s->xpos * inlink->w; + cy = s->ypos * inlink->h; + size = s->size * hypot(inlink->w, inlink->h); + s->width = s->tsize * (inlink->w); + + s->values = av_calloc(size, sizeof(*s->values)); + if (!s->values) + return AVERROR(ENOMEM); + + tilt = (s->tilt - 0.5) * M_PI; + s->x1 = cx - size / 2.0 * cosf(tilt); + s->x2 = cx + size / 2.0 * cosf(tilt); + s->y1 = cy - size / 2.0 * sinf(tilt); + s->y2 = cy + size / 2.0 * sinf(tilt); + s->ox = (inlink->w - s->width) >> 1; + s->oy = (inlink->h - 300) * s->ty; + + return 0; +} + +static void draw_scope(OscilloscopeContext *s, int x0, int y0, int x1, int y1, + AVFrame *out, PixelValues *p) +{ + int dx = FFABS(x1 - x0), sx = x0 < x1 ? 1 : -1; + int dy = FFABS(y1 - y0), sy = y0 < y1 ? 1 : -1; + int err = (dx > dy ? dx : -dy) / 2, e2; + + for (;;) { + if (x0 > 0 && y0 > 0 && x0 < out->width && y0 < out->height) { + FFDrawColor color = { { 0 } }; + int value[4] = { 0 }; + + s->pick_color(&s->draw, &color, out, x0, y0, value); + s->values[s->nb_values].p[0] = value[0]; + s->values[s->nb_values].p[1] = value[1]; + s->values[s->nb_values].p[2] = value[2]; + s->values[s->nb_values].p[3] = value[3]; + s->nb_values++; + + if (s->draw.nb_planes == 1) { + int i; + + for (i = 0; i < s->draw.pixelstep[0]; i++) + out->data[0][out->linesize[0] * y0 + x0 * s->draw.pixelstep[0] + i] = 255; + } else { + out->data[0][out->linesize[0] * y0 + x0] = 255; + } + } + + if (x0 == x1 && y0 == y1) + break; + + e2 = err; + + if (e2 >-dx) { + err -= dy; + x0 += sx; + } + + if (e2 < dy) { + err += dx; + y0 += sy; + } + } +} + +static void draw_line(FFDrawContext *draw, int x0, int y0, int x1, int y1, + AVFrame *out, FFDrawColor *color) +{ + int dx = FFABS(x1 - x0), sx = x0 < x1 ? 1 : -1; + int dy = FFABS(y1 - y0), sy = y0 < y1 ? 1 : -1; + int err = (dx > dy ? dx : -dy) / 2, e2; + int p, i; + + for (;;) { + for (p = 0; p < draw->nb_planes; p++) { + if (draw->nb_planes == 1) { + for (i = 0; i < 4; i++) { + out->data[0][y0 * out->linesize[0] + x0 * draw->pixelstep[0] + i] = color->comp[0].u8[i]; + } + } else { + out->data[p][out->linesize[p] * (y0 >> draw->vsub[p]) + (x0 >> draw->hsub[p])] = color->comp[p].u8[0]; + } + } + + if (x0 == x1 && y0 == y1) + break; + + e2 = err; + + if (e2 >-dx) { + err -= dy; + x0 += sx; + } + + if (e2 < dy) { + err += dx; + y0 += sy; + } + } +} + +static int oscilloscope_filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + AVFilterContext *ctx = inlink->dst; + OscilloscopeContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + float average[4] = { 0 }; + int max[4] = { 0 }; + int min[4] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX }; + int i, c; + + s->nb_values = 0; + draw_scope(s, s->x1, s->y1, s->x2, s->y2, frame, s->values); + ff_blend_rectangle(&s->draw, &s->dark, frame->data, frame->linesize, + frame->width, frame->height, + s->ox, s->oy, s->width, 300); + + if (s->grid) { + ff_fill_rectangle(&s->draw, &s->gray, frame->data, frame->linesize, + s->ox, s->oy, s->width - 1, 1); + + ff_fill_rectangle(&s->draw, &s->gray, frame->data, frame->linesize, + s->ox, s->oy + 64, s->width, 1); + + ff_fill_rectangle(&s->draw, &s->gray, frame->data, frame->linesize, + s->ox, s->oy + 128, s->width, 1); + + ff_fill_rectangle(&s->draw, &s->gray, frame->data, frame->linesize, + s->ox, s->oy + 192, s->width, 1); + + ff_fill_rectangle(&s->draw, &s->gray, frame->data, frame->linesize, + s->ox, s->oy + 256, s->width, 1); + + for (i = 0; i < 10; i++) { + ff_fill_rectangle(&s->draw, &s->gray, frame->data, frame->linesize, + s->ox + i * (s->width - 1) / 10, s->oy, 1, 256); + } + + ff_fill_rectangle(&s->draw, &s->gray, frame->data, frame->linesize, + s->ox + s->width - 1, s->oy, 1, 256); + } + + for (i = 1; i < s->nb_values; i++) { + for (c = 0; c < s->nb_comps; c++) { + if ((1 << c) & s->components) { + int x = i * s->width / s->nb_values; + int px = (i - 1) * s->width / s->nb_values; + int py = 255 - s->values[i-1].p[c]; + int y = 255 - s->values[i].p[c]; + + draw_line(&s->draw, s->ox + x, s->oy + y, s->ox + px, s->oy + py, frame, s->colors[c]); + } + } + } + + return ff_filter_frame(outlink, frame); +} + +static const AVFilterPad oscilloscope_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = oscilloscope_filter_frame, + .config_props = oscilloscope_config_input, + .needs_writable = 1, + }, + { NULL } +}; + +static const AVFilterPad oscilloscope_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +AVFilter ff_vf_oscilloscope = { + .name = "oscilloscope", + .description = NULL_IF_CONFIG_SMALL("2D Video Oscilloscope."), + .priv_size = sizeof(OscilloscopeContext), + .priv_class = &oscilloscope_class, + .query_formats = query_formats, + .uninit = oscilloscope_uninit, + .inputs = oscilloscope_inputs, + .outputs = oscilloscope_outputs, +};
Signed-off-by: Paul B Mahol <onemda@gmail.com> --- libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/vf_datascope.c | 299 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 301 insertions(+)