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