avfilter/f_drawgraph: add another slide mode
authorPaul B Mahol <onemda@gmail.com>
Tue, 26 Jul 2016 11:10:27 +0000 (13:10 +0200)
committerPaul B Mahol <onemda@gmail.com>
Thu, 28 Jul 2016 08:30:06 +0000 (10:30 +0200)
doc/filters.texi
libavfilter/f_drawgraph.c

index 5a64853..929cc33 100644 (file)
@@ -6438,6 +6438,9 @@ Scroll from right to left.
 
 @item rscroll
 Scroll from left to right.
+
+@item picture
+Draw single picture.
 @end table
 
 Default is @code{frame}.
index 0ca0d22..4c705fe 100644 (file)
@@ -44,6 +44,9 @@ typedef struct DrawGraphContext {
     int           x;
     int           prev_y[4];
     int           first;
+    float         *values[4];
+    int           values_size[4];
+    int           nb_values;
 } DrawGraphContext;
 
 #define OFFSET(x) offsetof(DrawGraphContext, x)
@@ -65,11 +68,12 @@ static const AVOption drawgraph_options[] = {
         {"bar", "draw bars", OFFSET(mode), AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "mode"},
         {"dot", "draw dots", OFFSET(mode), AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "mode"},
         {"line", "draw lines", OFFSET(mode), AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "mode"},
-    { "slide", "set slide mode", OFFSET(slide), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, FLAGS, "slide" },
+    { "slide", "set slide mode", OFFSET(slide), AV_OPT_TYPE_INT, {.i64=0}, 0, 4, FLAGS, "slide" },
         {"frame", "draw new frames", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "slide"},
         {"replace", "replace old columns with new", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "slide"},
         {"scroll", "scroll from right to left", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "slide"},
         {"rscroll", "scroll from left to right", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, FLAGS, "slide"},
+        {"picture", "display graph in single frame", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, FLAGS, "slide"},
     { "size", "set graph size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="900x256"}, 0, 0, FLAGS },
     { "s", "set graph size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="900x256"}, 0, 0, FLAGS },
     { NULL }
@@ -100,6 +104,18 @@ static av_cold int init(AVFilterContext *ctx)
 
     s->first = 1;
 
+    if (s->slide == 4) {
+        s->values[0] = av_fast_realloc(NULL, &s->values_size[0], 2000);
+        s->values[1] = av_fast_realloc(NULL, &s->values_size[1], 2000);
+        s->values[2] = av_fast_realloc(NULL, &s->values_size[2], 2000);
+        s->values[3] = av_fast_realloc(NULL, &s->values_size[3], 2000);
+
+        if (!s->values[0] || !s->values[1] ||
+            !s->values[2] || !s->values[3]) {
+            return AVERROR(ENOMEM);
+        }
+    }
+
     return 0;
 }
 
@@ -144,19 +160,45 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
     AVFrame *out = s->out;
     int i;
 
-    if (!s->out || s->out->width  != outlink->w ||
-                   s->out->height != outlink->h) {
-        av_frame_free(&s->out);
-        s->out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
-        out = s->out;
-        if (!s->out) {
-            av_frame_free(&in);
+    if (s->slide == 4 && s->nb_values >= s->values_size[0] / sizeof(float)) {
+        float *ptr;
+
+        ptr = av_fast_realloc(s->values[0], &s->values_size[0], s->values_size[0] * 2);
+        if (!ptr)
             return AVERROR(ENOMEM);
-        }
+        s->values[0] = ptr;
+
+        ptr = av_fast_realloc(s->values[1], &s->values_size[1], s->values_size[1] * 2);
+        if (!ptr)
+            return AVERROR(ENOMEM);
+        s->values[1] = ptr;
+
+        ptr = av_fast_realloc(s->values[2], &s->values_size[2], s->values_size[2] * 2);
+        if (!ptr)
+            return AVERROR(ENOMEM);
+        s->values[2] = ptr;
 
-        clear_image(s, out, outlink);
+        ptr = av_fast_realloc(s->values[3], &s->values_size[3], s->values_size[3] * 2);
+        if (!ptr)
+            return AVERROR(ENOMEM);
+        s->values[3] = ptr;
+    }
+
+    if (s->slide != 4 || s->nb_values == 0) {
+        if (!s->out || s->out->width  != outlink->w ||
+                       s->out->height != outlink->h) {
+            av_frame_free(&s->out);
+            s->out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+            out = s->out;
+            if (!s->out) {
+                av_frame_free(&in);
+                return AVERROR(ENOMEM);
+            }
+
+            clear_image(s, out, outlink);
+        }
+        av_frame_copy_props(out, in);
     }
-    av_frame_copy_props(out, in);
 
     metadata = av_frame_get_metadata(in);
 
@@ -166,6 +208,9 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
         uint32_t fg, bg;
         float vf;
 
+        if (s->slide == 4)
+            s->values[i][s->nb_values] = NAN;
+
         e = av_dict_get(metadata, s->key[i], NULL, 0);
         if (!e || !e->value)
             continue;
@@ -175,6 +220,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
 
         vf = av_clipf(vf, s->min, s->max);
 
+        if (s->slide == 4) {
+            s->values[i][s->nb_values] = vf;
+            continue;
+        }
+
         values[VAR_MIN] = s->min;
         values[VAR_MAX] = s->max;
         values[VAR_VAL] = vf;
@@ -255,12 +305,99 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
         }
     }
 
+    s->nb_values++;
     s->x++;
 
     av_frame_free(&in);
+
+    if (s->slide == 4)
+        return 0;
+
     return ff_filter_frame(outlink, av_frame_clone(s->out));
 }
 
+static int request_frame(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    DrawGraphContext *s = ctx->priv;
+    AVFrame *out = s->out;
+    int ret, i, k, step, l;
+
+    ret = ff_request_frame(ctx->inputs[0]);
+
+    if (s->slide == 4 && ret == AVERROR_EOF && s->nb_values > 0) {
+        s->x = l = 0;
+        step = ceil(s->nb_values / (float)s->w);
+
+        for (k = 0; k < s->nb_values; k++) {
+            for (i = 0; i < 4; i++) {
+                double values[VAR_VARS_NB];
+                int j, y, x, old;
+                uint32_t fg, bg;
+                float vf = s->values[i][k];
+
+                if (isnan(vf))
+                    continue;
+
+                values[VAR_MIN] = s->min;
+                values[VAR_MAX] = s->max;
+                values[VAR_VAL] = vf;
+
+                fg = av_expr_eval(s->fg_expr[i], values, NULL);
+                bg = AV_RN32(s->bg);
+
+                x = s->x;
+                y = (outlink->h - 1) * (1 - ((vf - s->min) / (s->max - s->min)));
+
+                switch (s->mode) {
+                case 0:
+                    old = AV_RN32(out->data[0] + y * out->linesize[0] + x * 4);
+                    for (j = y; j < outlink->h; j++) {
+                        if (old != bg &&
+                            (AV_RN32(out->data[0] + j * out->linesize[0] + x * 4) != old) ||
+                            AV_RN32(out->data[0] + FFMIN(j+1, outlink->h - 1) * out->linesize[0] + x * 4) != old) {
+                            draw_dot(fg, x, j, out);
+                            break;
+                        }
+                        draw_dot(fg, x, j, out);
+                    }
+                    break;
+                case 1:
+                    draw_dot(fg, x, y, out);
+                    break;
+                case 2:
+                    if (s->first) {
+                        s->first = 0;
+                        s->prev_y[i] = y;
+                    }
+
+                    if (y <= s->prev_y[i]) {
+                        for (j = y; j <= s->prev_y[i]; j++)
+                            draw_dot(fg, x, j, out);
+                    } else {
+                        for (j = s->prev_y[i]; j <= y; j++)
+                            draw_dot(fg, x, j, out);
+                    }
+                    s->prev_y[i] = y;
+                    break;
+                }
+            }
+
+            l++;
+            if (l >= step) {
+                l = 0;
+                s->x++;
+            }
+        }
+
+        s->nb_values = 0;
+        out->pts = 0;
+        ret = ff_filter_frame(ctx->outputs[0], s->out);
+    }
+
+    return ret;
+}
+
 static int config_output(AVFilterLink *outlink)
 {
     DrawGraphContext *s = outlink->src->priv;
@@ -279,7 +416,14 @@ static av_cold void uninit(AVFilterContext *ctx)
 
     for (i = 0; i < 4; i++)
         av_expr_free(s->fg_expr[i]);
-    av_frame_free(&s->out);
+
+    if (s->slide != 4)
+        av_frame_free(&s->out);
+
+    av_freep(&s->values[0]);
+    av_freep(&s->values[1]);
+    av_freep(&s->values[2]);
+    av_freep(&s->values[3]);
 }
 
 #if CONFIG_DRAWGRAPH_FILTER
@@ -300,6 +444,7 @@ static const AVFilterPad drawgraph_outputs[] = {
         .name         = "default",
         .type         = AVMEDIA_TYPE_VIDEO,
         .config_props = config_output,
+        .request_frame = request_frame,
     },
     { NULL }
 };
@@ -337,6 +482,7 @@ static const AVFilterPad adrawgraph_outputs[] = {
         .name         = "default",
         .type         = AVMEDIA_TYPE_VIDEO,
         .config_props = config_output,
+        .request_frame = request_frame,
     },
     { NULL }
 };