[FFmpeg-devel] [PATCH] Add applyfn filter.

Stefano Sabatini stefano.sabatini-lala
Sun Nov 28 16:43:54 CET 2010


Based on a patch by Zeger Knopf, <zeger AT customsoft DOT nl>.
---
 doc/filters.texi         |   16 +++
 libavfilter/Makefile     |    1 +
 libavfilter/allfilters.c |    1 +
 libavfilter/vf_applyfn.c |  269 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 287 insertions(+), 0 deletions(-)
 create mode 100644 libavfilter/vf_applyfn.c

diff --git a/doc/filters.texi b/doc/filters.texi
index 1cba2d6..8502ac0 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -71,6 +71,22 @@ build.
 
 Below is a description of the currently available video filters.
 
+ at section applyfn
+
+ at example
+# compute poor man gamma correction
+applyfn=y=255*pow(y/255\,y)
+
+# Assume the floor fn is available we can draw cartoons:
+applyfn=y=floor(y/64)*64:u=floor(u/64)*64:v=floor(v/64)*64
+
+# Assume the modulo fn is available we can draw checkerboars
+applyfn=y=(mod(floor(X/64)\,2)+mod(floor(Y/64)\,2))*y
+
+# B&W
+applyfn=u=128:v=128
+ at end example
+
 @section blackframe
 
 Detect frames that are (almost) completely black. Can be useful to
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 210510f..bb35921 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -19,6 +19,7 @@ OBJS-$(CONFIG_ANULLSRC_FILTER)               += asrc_anullsrc.o
 
 OBJS-$(CONFIG_ANULLSINK_FILTER)              += asink_anullsink.o
 
+OBJS-$(CONFIG_APPLYFN_FILTER)                += vf_applyfn.o
 OBJS-$(CONFIG_BLACKFRAME_FILTER)             += vf_blackframe.o
 OBJS-$(CONFIG_CROP_FILTER)                   += vf_crop.o
 OBJS-$(CONFIG_CROPDETECT_FILTER)             += vf_cropdetect.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index c3067b8..62538a7 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -40,6 +40,7 @@ void avfilter_register_all(void)
 
     REGISTER_FILTER (ANULLSINK,   anullsink,   asink);
 
+    REGISTER_FILTER (APPLYFN,     applyfn,     vf);
     REGISTER_FILTER (BLACKFRAME,  blackframe,  vf);
     REGISTER_FILTER (CROP,        crop,        vf);
     REGISTER_FILTER (CROPDETECT,  cropdetect,  vf);
diff --git a/libavfilter/vf_applyfn.c b/libavfilter/vf_applyfn.c
new file mode 100644
index 0000000..3f4b8f7
--- /dev/null
+++ b/libavfilter/vf_applyfn.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2009 Zeger Knops <zeger AT customsoft DOT nl>
+ * Copyright (c) 2010 Stefano Sabatini
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * Apply a function to each input pixel.
+ */
+
+#include "libavutil/eval.h"
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+#include "avfilter.h"
+
+enum VarName {
+    VAR_PHI,
+    VAR_PI,
+    VAR_E,
+    VAR_W,
+    VAR_H,
+    VAR_X,
+    VAR_Y,
+    VAR_n,
+    VAR_pts,
+    VAR_y,
+    VAR_u,
+    VAR_v,
+    VAR_a,
+    VAR_VARS_NB
+};
+
+static const char *var_names[]={
+    "PHI",
+    "PI",
+    "E",
+    "W",         ///< frame width
+    "H",         ///< frame height
+    "X",         ///< x position of current pixel
+    "Y",         ///< y position of current pixel
+    "n",         ///< frame number (starting at zero)
+    "pts",       ///< pts of frame
+    "y",         ///< Y value of current pixel
+    "u",         ///< U value of current pixel
+    "v",         ///< V value of current pixel
+    "a",         ///< A value of current pixel
+    NULL
+};
+
+typedef struct {
+    const AVClass *class;
+    char *y_expr_str, *u_expr_str, *v_expr_str, *a_expr_str,
+         *X_expr_str, *Y_expr_str;
+    AVExpr *y_expr, *u_expr, *v_expr, *a_expr,
+           *X_expr, *Y_expr;
+    int hsub, vsub;
+    double var_values[VAR_VARS_NB];
+} ApplyfnContext;
+
+#define OFFSET(x) offsetof(ApplyfnContext, x)
+
+static const AVOption applyfn_options[] = {
+{"y",  "set y expression", OFFSET(y_expr_str),  FF_OPT_TYPE_STRING, 0, CHAR_MIN, CHAR_MAX},
+{"u",  "set u expression", OFFSET(u_expr_str),  FF_OPT_TYPE_STRING, 0, CHAR_MIN, CHAR_MAX},
+{"v",  "set v expression", OFFSET(v_expr_str),  FF_OPT_TYPE_STRING, 0, CHAR_MIN, CHAR_MAX},
+{"a",  "set a expression", OFFSET(a_expr_str),  FF_OPT_TYPE_STRING, 0, CHAR_MIN, CHAR_MAX},
+{"x",  "set X expression", OFFSET(X_expr_str),  FF_OPT_TYPE_STRING, 0, CHAR_MIN, CHAR_MAX},
+{"y",  "set Y expression", OFFSET(Y_expr_str),  FF_OPT_TYPE_STRING, 0, CHAR_MIN, CHAR_MAX},
+{NULL},
+};
+
+static const char *applyfn_get_name(void *ctx)
+{
+    return "applyfn";
+}
+
+static const AVClass applyfn_class = {
+    "ApplyfnContext",
+    applyfn_get_name,
+    applyfn_options
+};
+
+static int init(AVFilterContext *ctx, const char *args, void *opaque)
+{
+    ApplyfnContext *applyfn = ctx->priv;
+    int ret;
+
+    applyfn->class = &applyfn_class;
+    av_opt_set_defaults2(applyfn, 0, 0);
+
+    applyfn->var_values[VAR_PHI] = M_PHI;
+    applyfn->var_values[VAR_PI]  = M_PI;
+    applyfn->var_values[VAR_E ]  = M_E;
+    applyfn->var_values[VAR_n ]  = -1;
+
+    applyfn->y_expr_str = av_strdup("y");
+    applyfn->u_expr_str = av_strdup("u");
+    applyfn->v_expr_str = av_strdup("v");
+    applyfn->a_expr_str = av_strdup("a");
+    applyfn->X_expr_str = av_strdup("X");
+    applyfn->Y_expr_str = av_strdup("Y");
+
+    if (args && (ret = av_set_options_string(applyfn, args, "=", ":")) < 0)
+        return ret;
+
+#define PARSE_EXPR(e)                                                   \
+    ret = av_expr_parse(&applyfn->e##_expr, applyfn->e##_expr_str,      \
+                         var_names, NULL, NULL, NULL, NULL, 0, ctx);    \
+    if (ret < 0) {                                                      \
+        av_log(applyfn, AV_LOG_ERROR,                                   \
+               "Error when evaluating the expression '%s' for '%s'.\n", \
+               #e, applyfn->e##_expr_str);                              \
+        return AVERROR(EINVAL);                                         \
+    }
+
+    PARSE_EXPR(y);
+    PARSE_EXPR(u);
+    PARSE_EXPR(v);
+    PARSE_EXPR(a);
+    PARSE_EXPR(X);
+    PARSE_EXPR(Y);
+
+    return 0;
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    ApplyfnContext *applyfn = ctx->priv;
+
+    av_free(applyfn->y_expr);
+    av_free(applyfn->u_expr);
+    av_free(applyfn->v_expr);
+    av_free(applyfn->a_expr);
+    av_free(applyfn->X_expr);
+    av_free(applyfn->Y_expr);
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    static const enum PixelFormat pix_fmts[] = {
+        PIX_FMT_YUV444P,  PIX_FMT_YUV422P,  PIX_FMT_YUV420P,
+        PIX_FMT_YUV411P,  PIX_FMT_YUV410P,
+        PIX_FMT_YUVJ444P, PIX_FMT_YUVJ422P, PIX_FMT_YUVJ420P,
+        PIX_FMT_YUV440P,  PIX_FMT_YUVJ440P,
+        PIX_FMT_YUVA420P,
+        PIX_FMT_NONE
+    };
+
+    avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts));
+    return 0;
+}
+
+static int config_input(AVFilterLink *inlink)
+{
+    ApplyfnContext *applyfn = inlink->dst->priv;
+
+    applyfn->hsub = av_pix_fmt_descriptors[inlink->format].log2_chroma_w;
+    applyfn->vsub = av_pix_fmt_descriptors[inlink->format].log2_chroma_h;
+
+    applyfn->var_values[VAR_W] = inlink->w;
+    applyfn->var_values[VAR_H] = inlink->h;
+
+    return 0;
+}
+
+static void start_frame(AVFilterLink *inlink, AVFilterBufferRef *picref)
+{
+    ApplyfnContext *applyfn = inlink->dst->priv;
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+
+    applyfn->var_values[VAR_pts] = picref->pts;
+    applyfn->var_values[VAR_n] += 1.0;
+
+    outlink->out_buf = avfilter_get_video_buffer(outlink, AV_PERM_WRITE, inlink->w, inlink->h);
+    avfilter_copy_buffer_ref_props(outlink->out_buf, picref);
+    avfilter_start_frame(outlink, avfilter_ref_buffer(outlink->out_buf, ~0));
+}
+
+#define Y 0
+#define U 1
+#define V 2
+#define A 3
+
+static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir)
+{
+    ApplyfnContext *applyfn = inlink->dst->priv;
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    AVFilterBufferRef *inpic  = inlink ->cur_buf;
+    AVFilterBufferRef *outpic = outlink->out_buf;
+    uint8_t *outrow[4];
+    int i, j;
+
+    for (i = 0; inpic->data[i]; i++) {
+        int vsub = i == 1 || i == 2 ? applyfn->vsub : 0;
+        outrow[i] = outpic->data[i] + (y>>vsub) * outpic->linesize[i];
+    }
+
+    for (i = 0; i < h; i++) {
+        applyfn->var_values[VAR_Y] = i;
+
+        for (j = 0; j < inlink->w; j++) {
+            uint16_t x_eval, y_eval;
+            int j_sub = j >> applyfn->hsub;
+
+            applyfn->var_values[VAR_X] = j;
+
+            x_eval = (uint16_t) av_expr_eval(applyfn->X_expr, applyfn->var_values, applyfn) % inlink->w;
+            y_eval = (uint16_t) av_expr_eval(applyfn->Y_expr, applyfn->var_values, applyfn) % inlink->h;
+
+            applyfn->var_values[VAR_y] = *(inpic->data[Y] +  y_eval                   * inpic->linesize[Y] +  x_eval);
+            applyfn->var_values[VAR_u] = *(inpic->data[U] + (y_eval >> applyfn->hsub) * inpic->linesize[U] + (x_eval >> applyfn->hsub));
+            applyfn->var_values[VAR_v] = *(inpic->data[V] + (y_eval >> applyfn->hsub) * inpic->linesize[V] + (x_eval >> applyfn->hsub));
+            applyfn->var_values[VAR_a] = inpic->data[A] ?
+                                         *(inpic->data[A] +  y_eval                   * inpic->linesize[A] +  x_eval): 0;
+
+            outrow[Y][j]     = av_expr_eval(applyfn->y_expr, applyfn->var_values, NULL);
+            outrow[U][j_sub] = av_expr_eval(applyfn->u_expr, applyfn->var_values, NULL);
+            outrow[V][j_sub] = av_expr_eval(applyfn->v_expr, applyfn->var_values, NULL);
+            if (outpic->data[A])
+                outrow[A][j] = av_expr_eval(applyfn->a_expr, applyfn->var_values, NULL);
+        }
+
+        outrow[Y] += outpic->linesize[Y];
+        if (((i+1) % (1<<applyfn->vsub)) == 0) {
+            outrow[U] += outpic->linesize[U];
+            outrow[V] += outpic->linesize[V];
+        }
+        if (outpic->data[A])
+            outrow[A] += outpic->linesize[A];
+    }
+
+    avfilter_draw_slice(outlink, y, h, slice_dir);
+}
+
+AVFilter avfilter_vf_applyfn = {
+    .name      = "applyfn",
+    .description = NULL_IF_CONFIG_SMALL("Apply a function to each input value."),
+    .priv_size = sizeof(ApplyfnContext),
+
+    .init          = init,
+    .uninit        = uninit,
+    .query_formats = query_formats,
+
+    .inputs    = (AVFilterPad[]) {{ .name            = "default",
+                                    .type            = AVMEDIA_TYPE_VIDEO,
+                                    .start_frame     = start_frame,
+                                    .draw_slice      = draw_slice,
+                                    .config_props    = config_input, },
+                                  { .name = NULL}},
+    .outputs   = (AVFilterPad[]) {{ .name            = "default",
+                                    .type            = AVMEDIA_TYPE_VIDEO, },
+                                  { .name = NULL}},
+};
+
-- 
1.7.1




More information about the ffmpeg-devel mailing list