Hi,

Here I attach a port of the Imlib2 vhook to the libavfilter API for
your kind review/testing.

I have not ported the -C option, as I favour the RGBA options.
The vf_imlib2.c source is diff'ed from /dev/null, as diff'ing from vf_negate.c
produces a less legible diff, IMHO.

The source contains a few usage examples, usable too as test scenarios.

Regards,
Víctor
Index: allfilters.h
===================================================================
--- allfilters.h	(revision 1447)
+++ allfilters.h	(working copy)
@@ -27,6 +27,7 @@
 extern AVFilter avfilter_vf_graph;
 extern AVFilter avfilter_vf_graphdesc;
 extern AVFilter avfilter_vf_graphfile;
+extern AVFilter avfilter_vf_imlib2;
 extern AVFilter avfilter_vf_negate;
 extern AVFilter avfilter_vf_overlay;
 extern AVFilter avfilter_vf_passthrough;
Index: avfilter.c
===================================================================
--- avfilter.c	(revision 1447)
+++ avfilter.c	(working copy)
@@ -340,6 +340,7 @@
     avfilter_register(&avfilter_vf_graph);
     avfilter_register(&avfilter_vf_graphdesc);
     avfilter_register(&avfilter_vf_graphfile);
+    avfilter_register(&avfilter_vf_imlib2);
     avfilter_register(&avfilter_vf_negate);
     avfilter_register(&avfilter_vf_overlay);
     avfilter_register(&avfilter_vf_passthrough);
Index: Makefile
===================================================================
--- Makefile	(revision 1447)
+++ Makefile	(working copy)
@@ -23,6 +23,12 @@
 
 EXTRALIBS := -L$(BUILD_ROOT)/libavutil -lavutil$(BUILDSUF) -L$(BUILD_ROOT)/libswscale -lswscale$(BUILDSUF) -L$(BUILD_ROOT)/libavcodec -lavcodec$(BUILDSUF) $(EXTRALIBS)
 
+ifeq ($(HAVE_IMLIB2),yes)
+    OBJS-yes += vf_imlib2.o
+    CFLAGS += `imlib2-config --cflags`
+    EXTRALIBS += `imlib2-config --libs`
+endif
+
 NAME=avfilter
 LIBVERSION=$(LAVFILTERVERSION)
 LIBMAJOR=$(LAVFILTERMAJOR)
--- /dev/null	2006-12-01 01:00:00.000000000 +0100
+++ vf_imlib2.c	2007-12-19 00:00:00.327862700 +0100
@@ -0,0 +1,413 @@
+/*
+ * Video Imlib2 filter (a port of the imlib2 vhook to the avfilter API)
+ * copyright (c) 2007 Victor Paesa
+ *
+ * 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
+ */
+
+ /*
+# Remember to set the path to your fonts
+FONTPATH="/cygdrive/c/WINDOWS/Fonts/"
+FONTPATH="$FONTPATH:/usr/share/imlib2/data/fonts/"
+FONTPATH="$FONTPATH:/usr/X11R6/lib/X11/fonts/TTF/"
+export FONTPATH
+
+# Bulb dancing in a Lissajous pattern
+ ffmpeg -i input.avi -vfilters \
+ 'imlib2=x:W*(0.5+0.25*sin(N/47*PI)):y:H*(0.5+0.50*cos(N/97*PI))-h/2:i:/usr/share/imlib2/data/images/bulb.png' \
+ output.avi
+
+ # Text scrolling
+ ffmpeg -i input.avi -vfilters \
+ imlib2='F:Vera.ttf/20:R:255:G:0:B:0:x:150+0.5*N:y:70+0.25*N:t:Text Scroll' \
+ output.avi
+
+ # Variable colors
+ ffmpeg -i input.avi -vfilters \
+ 'imlib2=F:Vera.ttf/20:R:abs(255*sin(N/47*PI)):G:abs(255*cos(N/47*PI)):B:abs(-255*sin(N/47*PI)):x:W/2:y:H/2:t:Variable colors' \
+ output.avi
+
+ # Anchor text to previous one
+ ffmpeg -i input.avi -vfilters \
+ 'imlib2=F:Vera.ttf/20:R:255:G:0:B:0:x:150+0.5*N:y:H-20-0.25*N:t:Anchored:G:255:y:Y+20:t:text' \
+ output.avi
+
+ # The full show
+ echo -e "Hello,\nI hope you will enjoy this.\nHave a nice day." > text.txt
+
+ SHOW='x:W*(0.5+0.25*sin(N/47*PI)):y:H*(0.5+0.50*cos(N/97*PI))-h/2:i:/usr/share/imlib2/data/images/bulb.png'
+ SHOW="$SHOW:F:Vera.ttf/20:R:255:G:0:B:0:x:150+0.5*N:y:70+0.25*N:t:Text Scroll"
+ SHOW="$SHOW:R:abs(255*sin(N/47*PI)):G:abs(255*cos(N/47*PI)):B:abs(-255*sin(N/47*PI)):x:W/2:y:H/2:t:Variable colors"
+ SHOW="$SHOW:R:255:G:0:B:0:x:150+0.5*N:y:H-60-0.5*N:t:Anchored:G:255:y:Y+20:t:text:y:Y+20:B:127:f:text.txt"
+ ffmpeg -i input.avi -vfilters \
+ imlib2="$SHOW" \
+ output.avi
+*/
+
+#include "avfilter.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <Imlib2.h>
+#include "eval.h"
+
+const char *const_names[]={
+    "PI",
+    "E",
+    "N",  // frame number (starting at zero)
+    "H",  // frame height
+    "W",  // frame width
+    "h",  // image height
+    "w",  // image width
+    "X",  // previous x
+    "Y",  // previous y
+    NULL
+};
+
+#define MAX_IMLIB2_CMD 256
+typedef struct
+{
+    char        cmd;
+    union
+    {
+        char      *str;
+        AVEvalExpr *exp;
+        Imlib_Image img;
+        Imlib_Font  fnt;
+    } data;
+} Imlib2Command;
+
+typedef struct
+{
+    int r, g, b, a;
+    int x, y;
+    int w, h, h_a, v_a;
+    int frame_number, num_cmd;
+    Imlib_Image image;
+    Imlib2Command cmd[MAX_IMLIB2_CMD];
+} Imlib2Context;
+
+static int init(AVFilterContext *ctx, const char *args, void *opaque)
+{
+    int chars_read;
+    char str[256], cmd[2], *argss, *error, *p, *fp;
+    Imlib2Context *imlib2 = ctx->priv;
+
+    imlib2->r = imlib2->g = imlib2->b = 0;
+    imlib2->a = 255;
+    imlib2->x = imlib2->y = imlib2->w = imlib2->h = 0;
+    imlib2->h_a = imlib2->v_a = 0;
+    imlib2->frame_number = 0;
+    imlib2->num_cmd = 0;
+
+    fp = getenv("FONTPATH");
+    /* Use ':' to split FONTPATH */
+    if (fp)
+        while (p = strchr(fp, ':')) {
+            *p = 0;
+            imlib_add_path_to_font_path(fp);
+            fp = p + 1;
+        }
+    if ((fp) && (*fp))
+        imlib_add_path_to_font_path(fp);
+
+    argss = args;
+    if(argss) {
+        while (2 == sscanf(argss, "%1[FtfxyiRGBA]:%255[^:]%n",
+                                    cmd, str, &chars_read)) {
+            //av_log(NULL, AV_LOG_INFO, "init() cmd:%d args:'%c:%s'\n", 
+            //                           imlib2->num_cmd, cmd[0], str);
+
+            if (imlib2->num_cmd>=MAX_IMLIB2_CMD) {
+                av_log(NULL, AV_LOG_ERROR, 
+                    "imlib2 init() cannot handle more than %d arguments\n",
+                    MAX_IMLIB2_CMD);
+                return -1;
+            }
+            imlib2->cmd[imlib2->num_cmd].cmd = cmd[0];
+            switch(cmd[0]) {
+            case 'F':
+                if (!(imlib2->cmd[imlib2->num_cmd].data.fnt = 
+                        imlib_load_font(str))) {
+                    av_log(NULL, AV_LOG_ERROR, 
+                           "imlib2 init() cannot load font '%s'\n", str);
+                    return -1;
+                }
+                break;
+            case 't':
+            case 'f':
+                imlib2->cmd[imlib2->num_cmd].data.str = av_strdup(str);
+                break;
+            case 'i':
+                if (!(imlib2->cmd[imlib2->num_cmd].data.img =
+                        imlib_load_image_immediately(str))) {
+                    av_log(NULL, AV_LOG_ERROR,
+                            "imlib2 init() cannot load image '%s'\n", str);
+                    return -1;
+                }
+                break;
+            case 'x':
+            case 'y':
+            case 'R':
+            case 'G':
+            case 'B':
+            case 'A':
+                if (!(imlib2->cmd[imlib2->num_cmd].data.exp =
+                        ff_parse(str, const_names, NULL, NULL, NULL, NULL, &error))) {
+                    av_log(NULL, AV_LOG_ERROR,
+                            "init() cannot parse expresion '%s' for '%c' : %s\n",
+                            str, cmd[0], error);
+                    return -1;
+                }
+                break;
+            }
+            imlib2->num_cmd++;
+            argss += chars_read;
+            if (*argss==':')
+                argss++;
+        }
+        return 0;
+    }
+    else
+        av_log(NULL, AV_LOG_ERROR,
+                "imlib2 init() expected arguments:'%s'\n", args);
+    return -1;
+}
+
+static int *query_formats(AVFilterLink *link)
+{
+    return avfilter_make_format_list(1, PIX_FMT_RGB32);
+}
+
+static void start_frame(AVFilterLink *link, AVFilterPicRef *picref)
+{
+    Imlib2Context *imlib2 = link->dst->priv;
+    AVFilterLink *out = NULL;
+
+    if(link->dst->output_count)
+        out = link->dst->outputs[0];
+
+    if(out) {
+        out->outpic   = avfilter_ref_pic(picref, ~0);
+        imlib2->image = imlib_create_image_using_data(
+            out->outpic->w, out->outpic->h, (DATA32 *)out->outpic->data[0]);
+        out->outpic->pts = picref->pts;
+        avfilter_start_frame(out, avfilter_ref_pic(out->outpic, ~0));
+    }
+}
+
+static void end_frame(AVFilterLink *link)
+{
+    Imlib2Context *imlib2 = link->dst->priv;
+    AVFilterLink *out = NULL;
+
+    if(link->dst->output_count)
+        out = link->dst->outputs[0];
+
+    imlib2->frame_number++;
+    imlib_context_set_image(imlib2->image);
+    imlib_free_image();
+    avfilter_unref_pic(link->cur_pic);
+    link->cur_pic = NULL;
+
+    if(out) {
+        if(out->outpic) {
+            avfilter_unref_pic(out->outpic);
+            out->outpic = NULL;
+        }
+        avfilter_end_frame(out);
+    }
+}
+
+static void draw_slice(AVFilterLink *link, int y, int h)
+{
+    Imlib2Context *imlib2 = link->dst->priv;
+    AVFilterPicRef *out = link->dst->outputs[0]->outpic;
+    int i;
+    char *p, *q;
+    char tbuff[1000];
+    char *tbp;
+
+
+    enum PosOfValue {
+        POV_LAST_H = 5,
+        POV_LAST_W,
+        POV_LAST_X,
+        POV_LAST_Y,
+    };
+    double const_values[]={
+        M_PI,
+        M_E,
+        imlib2->frame_number, // frame number (starting at zero)
+        out->h,               // frame height
+        out->w,               // frame width
+        imlib2->h,            // height of last image/text
+        imlib2->w,            // width  of last image/text
+        imlib2->x,            // previous x
+        imlib2->y,            // previous y
+        0.0
+    };
+
+    for (i=0; i < imlib2->num_cmd; i++) {
+        //av_log(NULL, AV_LOG_INFO, "draw_slice() cmd:%c pts:%lld\n",
+        //      imlib2->cmd[i].cmd, in->pts);
+        switch(imlib2->cmd[i].cmd) {
+        case 'F':
+            imlib_context_set_font(imlib2->cmd[i].data.fnt);
+            imlib_context_set_direction(IMLIB_TEXT_TO_RIGHT);
+            //TODO Store font metrics into const_names/values[]
+            break;
+        case 't':
+        case 'f':
+            if (imlib2->cmd[i].cmd == 't')
+                tbp = imlib2->cmd[i].data.str;
+            else {
+                int fd = open(imlib2->cmd[i].data.str, O_RDONLY);
+        
+                if (fd < 0) {
+                    tbp = "[File not found]";
+                } else {
+                    int l = read(fd, tbuff, sizeof(tbuff) - 1);
+       
+                    if (l >= 0) {
+                        tbuff[l] = 0;
+                        tbp = tbuff;
+                    } else {
+                        tbp = "[I/O Error]";
+                    }
+                    close(fd);
+                }
+            }
+            for (p = tbp; p; p = q) {
+                q = strchr(p, '\n');
+                if (q)
+                    *q = 0;
+
+                imlib_context_set_image(imlib2->image);
+                imlib_text_draw_with_return_metrics(imlib2->x, imlib2->y, p,
+                        &imlib2->w, &imlib2->h, &imlib2->h_a, &imlib2->v_a);
+                //TODO Store text metrics into const_names/values[]
+                if (q) {
+                    imlib2->y += imlib2->v_a;
+                    q++;
+                }
+            }
+            const_values[POV_LAST_Y] = imlib2->y;
+            const_values[POV_LAST_W] = imlib2->w;
+            const_values[POV_LAST_H] = imlib2->h;
+            break;
+        case 'i':
+            imlib_context_set_image(imlib2->cmd[i].data.img);
+            const_values[POV_LAST_W] = imlib2->w = imlib_image_get_width();
+            const_values[POV_LAST_H] = imlib2->h = imlib_image_get_height();
+            imlib_context_set_image(imlib2->image);
+            imlib_blend_image_onto_image(imlib2->cmd[i].data.img, 0,
+                0, 0, imlib2->w, imlib2->h,
+                imlib2->x, imlib2->y, imlib2->w, imlib2->h);
+            break;
+        case 'x':
+            const_values[POV_LAST_X] = imlib2->x =
+                ff_parse_eval(imlib2->cmd[i].data.exp, const_values, imlib2);
+            break;
+        case 'y':
+            const_values[POV_LAST_Y] = imlib2->y =
+                ff_parse_eval(imlib2->cmd[i].data.exp, const_values, imlib2);
+            break;
+        case 'R':
+            imlib2->r =
+                ff_parse_eval(imlib2->cmd[i].data.exp, const_values, imlib2);
+            imlib_context_set_color(imlib2->r, imlib2->g, imlib2->b, imlib2->a);
+            break;
+        case 'G':
+            imlib2->g =
+                ff_parse_eval(imlib2->cmd[i].data.exp, const_values, imlib2);
+            imlib_context_set_color(imlib2->r, imlib2->g, imlib2->b, imlib2->a);
+            break;
+        case 'B':
+            imlib2->b =
+                ff_parse_eval(imlib2->cmd[i].data.exp, const_values, imlib2);
+            imlib_context_set_color(imlib2->r, imlib2->g, imlib2->b, imlib2->a);
+            break;
+        case 'A':
+            imlib2->a =
+                ff_parse_eval(imlib2->cmd[i].data.exp, const_values, imlib2);
+            imlib_context_set_color(imlib2->r, imlib2->g, imlib2->b, imlib2->a);
+            break;
+        }
+
+    }
+
+    avfilter_draw_slice(link->dst->outputs[0], y, h);
+}
+
+static void uninit(AVFilterContext *ctx)
+{
+    int i;
+    Imlib2Context *imlib2 = ctx->priv;
+        
+    for (i=0; i < imlib2->num_cmd; i++) {
+        switch(imlib2->cmd[i].cmd) {
+        case 'F':
+            imlib_context_set_font(imlib2->cmd[i].data.fnt);
+            imlib_free_font();
+            break;
+        case 't':
+        case 'f':
+            av_free(imlib2->cmd[i].data.str);
+            break;
+        case 'i':
+            imlib_context_set_image(imlib2->cmd[i].data.img);
+            imlib_free_image();
+            break;
+        case 'x':
+        case 'y':
+        case 'R':
+        case 'G':
+        case 'B':
+        case 'A':
+            ff_eval_free(imlib2->cmd[i].data.exp);
+            break;
+        }
+    }
+}
+
+AVFilter avfilter_vf_imlib2 =
+{
+    .name      = "imlib2",
+    .author    = "Victor Paesa",
+
+    .init      = init,
+    .uninit    = uninit,
+
+    .priv_size = sizeof(Imlib2Context),
+
+    .inputs    = (AVFilterPad[]) {{ .name            = "default",
+                                    .type            = AV_PAD_VIDEO,
+                                    .start_frame     = start_frame,
+                                    .draw_slice      = draw_slice,
+                                    .query_formats   = query_formats,
+                                    .end_frame       = end_frame,
+                                    .min_perms       = AV_PERM_READ |
+                                                       AV_PERM_WRITE, },
+                                  { .name = NULL}},
+    .outputs   = (AVFilterPad[]) {{ .name            = "default",
+                                    .type            = AV_PAD_VIDEO, },
+                                  { .name = NULL}},
+};
_______________________________________________
FFmpeg-soc mailing list
[email protected]
http://lists.mplayerhq.hu/mailman/listinfo/ffmpeg-soc

Reply via email to