X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavfilter%2Fvf_datascope.c;h=88007c92656d0a56fb6e54d333273ebc05730267;hb=d40bb518b50561db60ef71ab0e37eb7f3fb9043b;hp=e91a1e66cf5c742d4b7ce0e0968ef22f1433b93d;hpb=103effebc1f10d69c311a02794c8150a90bc928c;p=ffmpeg diff --git a/libavfilter/vf_datascope.c b/libavfilter/vf_datascope.c index e91a1e66cf5..88007c92656 100644 --- a/libavfilter/vf_datascope.c +++ b/libavfilter/vf_datascope.c @@ -35,7 +35,9 @@ typedef struct DatascopeContext { int ow, oh; int x, y; int mode; + int dformat; int axis; + int components; float opacity; int nb_planes; @@ -59,14 +61,18 @@ typedef struct DatascopeContext { static const AVOption datascope_options[] = { { "size", "set output size", OFFSET(ow), AV_OPT_TYPE_IMAGE_SIZE, {.str="hd720"}, 0, 0, FLAGS }, { "s", "set output size", OFFSET(ow), AV_OPT_TYPE_IMAGE_SIZE, {.str="hd720"}, 0, 0, FLAGS }, - { "x", "set x offset", OFFSET(x), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS }, - { "y", "set y offset", OFFSET(y), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS }, - { "mode", "set scope mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, "mode" }, - { "mono", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "mode" }, - { "color", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "mode" }, - { "color2", NULL, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "mode" }, - { "axis", "draw column/row numbers", OFFSET(axis), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, - { "opacity", "set background opacity", OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=0.75}, 0, 1, FLAGS }, + { "x", "set x offset", OFFSET(x), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGSR }, + { "y", "set y offset", OFFSET(y), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGSR }, + { "mode", "set scope mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGSR, "mode" }, + { "mono", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGSR, "mode" }, + { "color", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGSR, "mode" }, + { "color2", NULL, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGSR, "mode" }, + { "axis", "draw column/row numbers", OFFSET(axis), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGSR }, + { "opacity", "set background opacity", OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=0.75}, 0, 1, FLAGSR }, + { "format", "set display number format", OFFSET(dformat), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGSR, "format" }, + { "hex", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGSR, "format" }, + { "dec", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGSR, "format" }, + { "components", "set components to display", OFFSET(components), AV_OPT_TYPE_INT, {.i64=15}, 1, 15, FLAGSR }, { NULL } }; @@ -165,7 +171,7 @@ static void reverse_color16(FFDrawContext *draw, FFDrawColor *color, FFDrawColor typedef struct ThreadData { AVFrame *in, *out; - int xoff, yoff; + int xoff, yoff, PP; } ThreadData; static int filter_color2(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) @@ -176,13 +182,15 @@ static int filter_color2(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs ThreadData *td = arg; AVFrame *in = td->in; AVFrame *out = td->out; + const int PP = td->PP; const int xoff = td->xoff; const int yoff = td->yoff; const int P = FFMAX(s->nb_planes, s->nb_comps); const int C = s->chars; + const int D = ((s->chars - s->dformat) >> 2) + s->dformat * 2; const int W = (outlink->w - xoff) / (C * 10); - const int H = (outlink->h - yoff) / (P * 12); - const char *format[2] = {"%02X\n", "%04X\n"}; + const int H = (outlink->h - yoff) / (PP * 12); + const char *format[4] = {"%02X\n", "%04X\n", "%03d\n", "%05d\n"}; const int slice_start = (W * jobnr) / nb_jobs; const int slice_end = (W * (jobnr+1)) / nb_jobs; int x, y, p; @@ -191,18 +199,21 @@ static int filter_color2(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs for (x = slice_start; x < slice_end && (x + s->x < inlink->w); x++) { FFDrawColor color = { { 0 } }; FFDrawColor reverse = { { 0 } }; - int value[4] = { 0 }; + int value[4] = { 0 }, pp = 0; s->pick_color(&s->draw, &color, in, x + s->x, y + s->y, value); s->reverse_color(&s->draw, &color, &reverse); ff_fill_rectangle(&s->draw, &color, out->data, out->linesize, - xoff + x * C * 10, yoff + y * P * 12, C * 10, P * 12); + xoff + x * C * 10, yoff + y * PP * 12, C * 10, PP * 12); for (p = 0; p < P; p++) { char text[256]; - snprintf(text, sizeof(text), format[C>>2], value[p]); - draw_text(&s->draw, out, &reverse, xoff + x * C * 10 + 2, yoff + y * P * 12 + p * 10 + 2, text, 0); + if (!(s->components & (1 << p))) + continue; + snprintf(text, sizeof(text), format[D], value[p]); + draw_text(&s->draw, out, &reverse, xoff + x * C * 10 + 2, yoff + y * PP * 12 + pp * 10 + 2, text, 0); + pp++; } } } @@ -218,13 +229,15 @@ static int filter_color(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) ThreadData *td = arg; AVFrame *in = td->in; AVFrame *out = td->out; + const int PP = td->PP; const int xoff = td->xoff; const int yoff = td->yoff; const int P = FFMAX(s->nb_planes, s->nb_comps); const int C = s->chars; + const int D = ((s->chars - s->dformat) >> 2) + s->dformat * 2; const int W = (outlink->w - xoff) / (C * 10); - const int H = (outlink->h - yoff) / (P * 12); - const char *format[2] = {"%02X\n", "%04X\n"}; + const int H = (outlink->h - yoff) / (PP * 12); + const char *format[4] = {"%02X\n", "%04X\n", "%03d\n", "%05d\n"}; const int slice_start = (W * jobnr) / nb_jobs; const int slice_end = (W * (jobnr+1)) / nb_jobs; int x, y, p; @@ -232,15 +245,18 @@ static int filter_color(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) for (y = 0; y < H && (y + s->y < inlink->h); y++) { for (x = slice_start; x < slice_end && (x + s->x < inlink->w); x++) { FFDrawColor color = { { 0 } }; - int value[4] = { 0 }; + int value[4] = { 0 }, pp = 0; s->pick_color(&s->draw, &color, in, x + s->x, y + s->y, value); for (p = 0; p < P; p++) { char text[256]; - snprintf(text, sizeof(text), format[C>>2], value[p]); - draw_text(&s->draw, out, &color, xoff + x * C * 10 + 2, yoff + y * P * 12 + p * 10 + 2, text, 0); + if (!(s->components & (1 << p))) + continue; + snprintf(text, sizeof(text), format[D], value[p]); + draw_text(&s->draw, out, &color, xoff + x * C * 10 + 2, yoff + y * PP * 12 + pp * 10 + 2, text, 0); + pp++; } } } @@ -256,13 +272,15 @@ static int filter_mono(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) ThreadData *td = arg; AVFrame *in = td->in; AVFrame *out = td->out; + const int PP = td->PP; const int xoff = td->xoff; const int yoff = td->yoff; const int P = FFMAX(s->nb_planes, s->nb_comps); const int C = s->chars; + const int D = ((s->chars - s->dformat) >> 2) + s->dformat * 2; const int W = (outlink->w - xoff) / (C * 10); - const int H = (outlink->h - yoff) / (P * 12); - const char *format[2] = {"%02X\n", "%04X\n"}; + const int H = (outlink->h - yoff) / (PP * 12); + const char *format[4] = {"%02X\n", "%04X\n", "%03d\n", "%05d\n"}; const int slice_start = (W * jobnr) / nb_jobs; const int slice_end = (W * (jobnr+1)) / nb_jobs; int x, y, p; @@ -270,14 +288,17 @@ static int filter_mono(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) for (y = 0; y < H && (y + s->y < inlink->h); y++) { for (x = slice_start; x < slice_end && (x + s->x < inlink->w); x++) { FFDrawColor color = { { 0 } }; - int value[4] = { 0 }; + int value[4] = { 0 }, pp = 0; s->pick_color(&s->draw, &color, in, x + s->x, y + s->y, value); for (p = 0; p < P; p++) { char text[256]; - snprintf(text, sizeof(text), format[C>>2], value[p]); - draw_text(&s->draw, out, &s->white, xoff + x * C * 10 + 2, yoff + y * P * 12 + p * 10 + 2, text, 0); + if (!(s->components & (1 << p))) + continue; + snprintf(text, sizeof(text), format[D], value[p]); + draw_text(&s->draw, out, &s->white, xoff + x * C * 10 + 2, yoff + y * PP * 12 + pp * 10 + 2, text, 0); + pp++; } } } @@ -290,9 +311,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) AVFilterContext *ctx = inlink->dst; DatascopeContext *s = ctx->priv; AVFilterLink *outlink = ctx->outputs[0]; + const int P = FFMAX(s->nb_planes, s->nb_comps); ThreadData td = { 0 }; int ymaxlen = 0; int xmaxlen = 0; + int PP = 0; AVFrame *out; out = ff_get_video_buffer(outlink, outlink->w, outlink->h); @@ -305,10 +328,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) ff_fill_rectangle(&s->draw, &s->black, out->data, out->linesize, 0, 0, outlink->w, outlink->h); + for (int p = 0; p < P; p++) { + if (s->components & (1 << p)) + PP++; + } + PP = FFMAX(PP, 1); + if (s->axis) { - const int P = FFMAX(s->nb_planes, s->nb_comps); const int C = s->chars; - int Y = outlink->h / (P * 12); + int Y = outlink->h / (PP * 12); int X = outlink->w / (C * 10); char text[256] = { 0 }; int x, y; @@ -320,16 +348,16 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) xmaxlen = strlen(text); xmaxlen *= 10; - Y = (outlink->h - xmaxlen) / (P * 12); + Y = (outlink->h - xmaxlen) / (PP * 12); X = (outlink->w - ymaxlen) / (C * 10); for (y = 0; y < Y; y++) { snprintf(text, sizeof(text), "%d", s->y + y); ff_fill_rectangle(&s->draw, &s->gray, out->data, out->linesize, - 0, xmaxlen + y * P * 12 + (P + 1) * P - 2, ymaxlen, 10); + 0, xmaxlen + y * PP * 12 + (PP + 1) * PP - 2, ymaxlen, 10); - draw_text(&s->draw, out, &s->yellow, 2, xmaxlen + y * P * 12 + (P + 1) * P, text, 0); + draw_text(&s->draw, out, &s->yellow, 2, xmaxlen + y * PP * 12 + (PP + 1) * PP, text, 0); } for (x = 0; x < X; x++) { @@ -342,7 +370,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) } } - td.in = in; td.out = out, td.yoff = xmaxlen, td.xoff = ymaxlen; + td.in = in; td.out = out, td.yoff = xmaxlen, td.xoff = ymaxlen, td.PP = PP; ctx->internal->execute(ctx, s->filter, &td, NULL, FFMIN(ff_filter_get_nb_threads(ctx), FFMAX(outlink->w / 20, 1))); av_frame_free(&in); @@ -360,7 +388,7 @@ static int config_input(AVFilterLink *inlink) ff_draw_color(&s->draw, &s->black, (uint8_t[]){ 0, 0, 0, alpha} ); ff_draw_color(&s->draw, &s->yellow, (uint8_t[]){ 255, 255, 0, 255} ); ff_draw_color(&s->draw, &s->gray, (uint8_t[]){ 77, 77, 77, 255} ); - s->chars = (s->draw.desc->comp[0].depth + 7) / 8 * 2; + s->chars = (s->draw.desc->comp[0].depth + 7) / 8 * 2 + s->dformat; s->nb_comps = s->draw.desc->nb_components; switch (s->mode) { @@ -391,6 +419,18 @@ static int config_output(AVFilterLink *outlink) return 0; } +static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, + char *res, int res_len, int flags) +{ + int ret; + + ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); + if (ret < 0) + return ret; + + return config_input(ctx->inputs[0]); +} + static const AVFilterPad inputs[] = { { .name = "default", @@ -419,6 +459,7 @@ AVFilter ff_vf_datascope = { .inputs = inputs, .outputs = outputs, .flags = AVFILTER_FLAG_SLICE_THREADS, + .process_command = process_command, }; typedef struct PixscopeContext { @@ -445,19 +486,21 @@ typedef struct PixscopeContext { FFDrawColor red; FFDrawColor *colors[4]; + uint16_t values[4][80][80]; + void (*pick_color)(FFDrawContext *draw, FFDrawColor *color, AVFrame *in, int x, int y, int *value); } PixscopeContext; #define POFFSET(x) offsetof(PixscopeContext, x) static const AVOption pixscope_options[] = { - { "x", "set scope x offset", POFFSET(xpos), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGS }, - { "y", "set scope y offset", POFFSET(ypos), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGS }, - { "w", "set scope width", POFFSET(w), AV_OPT_TYPE_INT, {.i64=7}, 1, 80, FLAGS }, - { "h", "set scope height", POFFSET(h), AV_OPT_TYPE_INT, {.i64=7}, 1, 80, FLAGS }, - { "o", "set window opacity", POFFSET(o), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGS }, - { "wx", "set window x offset", POFFSET(wx), AV_OPT_TYPE_FLOAT, {.dbl=-1}, -1, 1, FLAGS }, - { "wy", "set window y offset", POFFSET(wy), AV_OPT_TYPE_FLOAT, {.dbl=-1}, -1, 1, FLAGS }, + { "x", "set scope x offset", POFFSET(xpos), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGSR }, + { "y", "set scope y offset", POFFSET(ypos), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGSR }, + { "w", "set scope width", POFFSET(w), AV_OPT_TYPE_INT, {.i64=7}, 1, 80, FLAGSR }, + { "h", "set scope height", POFFSET(h), AV_OPT_TYPE_INT, {.i64=7}, 1, 80, FLAGSR }, + { "o", "set window opacity", POFFSET(o), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGSR }, + { "wx", "set window x offset", POFFSET(wx), AV_OPT_TYPE_FLOAT, {.dbl=-1}, -1, 1, FLAGSR }, + { "wy", "set window y offset", POFFSET(wy), AV_OPT_TYPE_FLOAT, {.dbl=-1}, -1, 1, FLAGSR }, { NULL } }; @@ -519,6 +562,8 @@ static int pixscope_config_input(AVFilterLink *inlink) return 0; } +#define SQR(x) ((x)*(x)) + static int pixscope_filter_frame(AVFilterLink *inlink, AVFrame *in) { AVFilterContext *ctx = inlink->dst; @@ -527,7 +572,7 @@ static int pixscope_filter_frame(AVFilterLink *inlink, AVFrame *in) AVFrame *out = ff_get_video_buffer(outlink, in->width, in->height); int max[4] = { 0 }, min[4] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX }; float average[4] = { 0 }; - double rms[4] = { 0 }; + double std[4] = { 0 }, rms[4] = { 0 }; const char rgba[4] = { 'R', 'G', 'B', 'A' }; const char yuva[4] = { 'Y', 'U', 'V', 'A' }; int x, y, X, Y, i, w, h; @@ -584,6 +629,7 @@ static int pixscope_filter_frame(AVFilterLink *inlink, AVFrame *in) ff_fill_rectangle(&s->draw, &color, out->data, out->linesize, x * w + (s->ww - 4 - (s->w * w)) / 2 + X, y * h + 2 + Y, w, h); for (i = 0; i < 4; i++) { + s->values[i][x][y] = value[i]; rms[i] += (double)value[i] * (double)value[i]; average[i] += value[i]; min[i] = FFMIN(min[i], value[i]); @@ -630,19 +676,51 @@ static int pixscope_filter_frame(AVFilterLink *inlink, AVFrame *in) average[i] /= s->w * s->h; } + for (y = 0; y < s->h; y++) { + for (x = 0; x < s->w; x++) { + for (i = 0; i < 4; i++) + std[i] += SQR(s->values[i][x][y] - average[i]); + } + } + + for (i = 0; i < 4; i++) { + std[i] /= s->w * s->h; + std[i] = sqrt(std[i]); + } + snprintf(text, sizeof(text), "CH AVG MIN MAX RMS\n"); - draw_text(&s->draw, out, &s->white, X + 28, Y + s->ww + 20, text, 0); + draw_text(&s->draw, out, &s->white, X + 28, Y + s->ww + 5, text, 0); for (i = 0; i < s->nb_comps; i++) { int c = s->rgba_map[i]; snprintf(text, sizeof(text), "%c %07.1f %05d %05d %07.1f\n", s->is_rgb ? rgba[i] : yuva[i], average[c], min[c], max[c], rms[c]); - draw_text(&s->draw, out, s->colors[i], X + 28, Y + s->ww + 20 * (i + 2), text, 0); + draw_text(&s->draw, out, s->colors[i], X + 28, Y + s->ww + 15 * (i + 1), text, 0); + } + snprintf(text, sizeof(text), "CH STD\n"); + draw_text(&s->draw, out, &s->white, X + 28, Y + s->ww + 15 * (0 + 5), text, 0); + for (i = 0; i < s->nb_comps; i++) { + int c = s->rgba_map[i]; + + snprintf(text, sizeof(text), "%c %07.2f\n", s->is_rgb ? rgba[i] : yuva[i], std[c]); + draw_text(&s->draw, out, s->colors[i], X + 28, Y + s->ww + 15 * (i + 6), text, 0); } av_frame_free(&in); return ff_filter_frame(outlink, out); } +static int pixscope_process_command(AVFilterContext *ctx, const char *cmd, const char *args, + char *res, int res_len, int flags) +{ + int ret; + + ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); + if (ret < 0) + return ret; + + return pixscope_config_input(ctx->inputs[0]); +} + static const AVFilterPad pixscope_inputs[] = { { .name = "default", @@ -670,6 +748,7 @@ AVFilter ff_vf_pixscope = { .inputs = pixscope_inputs, .outputs = pixscope_outputs, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, + .process_command = pixscope_process_command, }; typedef struct PixelValues { @@ -759,7 +838,7 @@ static void draw_line(FFDrawContext *draw, int x0, int y0, int x1, int y1, for (p = 0; p < draw->nb_planes; p++) { if (draw->desc->comp[p].depth == 8) { if (draw->nb_planes == 1) { - for (i = 0; i < 4; i++) { + for (i = 0; i < draw->desc->nb_components; i++) { out->data[0][y0 * out->linesize[0] + x0 * draw->pixelstep[0] + i] = color->comp[0].u8[i]; } } else { @@ -767,8 +846,8 @@ static void draw_line(FFDrawContext *draw, int x0, int y0, int x1, int y1, } } else { if (draw->nb_planes == 1) { - for (i = 0; i < 4; i++) { - AV_WN16(out->data[0] + y0 * out->linesize[0] + 2 * (x0 * draw->pixelstep[0] + i), color->comp[0].u16[i]); + for (i = 0; i < draw->desc->nb_components; i++) { + AV_WN16(out->data[0] + y0 * out->linesize[0] + (x0 * draw->pixelstep[0] + i), color->comp[0].u16[i]); } } else { AV_WN16(out->data[p] + out->linesize[p] * (y0 >> draw->vsub[p]) + (x0 >> draw->hsub[p]) * 2, color->comp[p].u16[0]); @@ -933,7 +1012,7 @@ static void draw_scope(OscilloscopeContext *s, int x0, int y0, int x1, int y1, if (s->draw.nb_planes == 1) { int i; - for (i = 0; i < s->draw.pixelstep[0]; i++) + for (i = 0; i < s->nb_comps; i++) out->data[0][out->linesize[0] * y0 + x0 * s->draw.pixelstep[0] + i] = 255 * ((s->nb_values + state) & 1); } else { out->data[0][out->linesize[0] * y0 + x0] = 255 * ((s->nb_values + state) & 1); @@ -942,8 +1021,8 @@ static void draw_scope(OscilloscopeContext *s, int x0, int y0, int x1, int y1, if (s->draw.nb_planes == 1) { int i; - for (i = 0; i < s->draw.pixelstep[0]; i++) - AV_WN16(out->data[0] + out->linesize[0] * y0 + 2 * x0 * (s->draw.pixelstep[0] + i), (s->max - 1) * ((s->nb_values + state) & 1)); + for (i = 0; i < s->nb_comps; i++) + AV_WN16(out->data[0] + out->linesize[0] * y0 + x0 * s->draw.pixelstep[0] + i, (s->max - 1) * ((s->nb_values + state) & 1)); } else { AV_WN16(out->data[0] + out->linesize[0] * y0 + 2 * x0, (s->max - 1) * ((s->nb_values + state) & 1)); }