[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