vhook/imlib2.c

Go to the documentation of this file.
00001 /*
00002  * imlib2 based hook
00003  * Copyright (c) 2002 Philip Gladstone
00004  *
00005  * This module is very much intended as an example of what could be done.
00006  *
00007  * One caution is that this is an expensive process -- in particular the
00008  * conversion of the image into RGB and back is time consuming. For some
00009  * special cases -- e.g. painting black text -- it would be faster to paint
00010  * the text into a bitmap and then combine it directly into the YUV
00011  * image. However, this code is fast enough to handle 10 fps of 320x240 on a
00012  * 900MHz Duron in maybe 15% of the CPU.
00013 
00014  * See further statistics on Pentium4, 3GHz, FFMpeg is SVN-r6798
00015  * Input movie is 20.2 seconds of PAL DV on AVI
00016  * Output movie is DVD compliant VOB.
00017  *
00018  ffmpeg -i input.avi -target pal-dvd out.vob
00019  # 13.516s just transcode
00020  ffmpeg -i input.avi -vhook /usr/local/bin/vhook/null.dll -target pal-dvd out.vob
00021  # 23.546s transcode and img_convert
00022  ffmpeg -i input.avi -vhook \
00023  'vhook/imlib2.dll -c red -F Vera/20 -x 150-0.5*N -y 70+0.25*N -t Hello_person' \
00024  -target pal-dvd out.vob
00025  # 21.454s transcode, img_convert and move text around
00026  ffmpeg -i input.avi -vhook \
00027  'vhook/imlib2.dll -x 150-0.5*N -y 70+0.25*N -i /usr/share/imlib2/data/images/bulb.png' \
00028  -target pal-dvd out.vob
00029  # 20.828s transcode, img_convert and move image around
00030  *
00031  * This file is part of FFmpeg.
00032  *
00033  * FFmpeg is free software; you can redistribute it and/or
00034  * modify it under the terms of the GNU Lesser General Public
00035  * License as published by the Free Software Foundation; either
00036  * version 2.1 of the License, or (at your option) any later version.
00037  *
00038  * FFmpeg is distributed in the hope that it will be useful,
00039  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00040  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00041  * Lesser General Public License for more details.
00042  *
00043  * You should have received a copy of the GNU Lesser General Public
00044  * License along with FFmpeg; if not, write to the Free Software
00045  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00046  */
00047 
00048 #include "libavformat/framehook.h"
00049 #include "libswscale/swscale.h"
00050 
00051 #include <stdio.h>
00052 #include <stdlib.h>
00053 #include <fcntl.h>
00054 #include <stdarg.h>
00055 #include <string.h>
00056 #include <strings.h>
00057 #include <unistd.h>
00058 #undef time
00059 #include <sys/time.h>
00060 #include <time.h>
00061 #include <Imlib2.h>
00062 #include "libavcodec/eval.h"
00063 
00064 const char *const_names[]={
00065 "PI",
00066 "E",
00067 "N", // frame number (starting at zero)
00068 "H", // frame height
00069 "W", // frame width
00070 "h", // image height
00071 "w", // image width
00072 "X", // previous x
00073 "Y", // previous y
00074 NULL
00075 };
00076 
00077 static int sws_flags = SWS_BICUBIC;
00078 
00079 typedef struct {
00080 int dummy;
00081 Imlib_Font fn;
00082 char *text;
00083 char *file;
00084 int r, g, b, a;
00085 AVEvalExpr *eval_r, *eval_g, *eval_b, *eval_a;
00086 char *expr_R, *expr_G, *expr_B, *expr_A;
00087 int eval_colors;
00088 double x, y;
00089 char *fileImage;
00090 struct CachedImage *cache;
00091 Imlib_Image imageOverlaid;
00092 AVEvalExpr *eval_x, *eval_y;
00093 char *expr_x, *expr_y;
00094 int frame_number;
00095 int imageOverlaid_width, imageOverlaid_height;
00096 
00097 // This vhook first converts frame to RGB ...
00098 struct SwsContext *toRGB_convert_ctx;
00099 // ... and then converts back frame from RGB to initial format
00100 struct SwsContext *fromRGB_convert_ctx;
00101 } ContextInfo;
00102 
00103 typedef struct CachedImage {
00104 struct CachedImage *next;
00105 Imlib_Image image;
00106 int width;
00107 int height;
00108 } CachedImage;
00109 
00110 void Release(void *ctx)
00111 {
00112 ContextInfo *ci;
00113 ci = (ContextInfo *) ctx;
00114 
00115 if (ci->cache) {
00116 imlib_context_set_image(ci->cache->image);
00117 imlib_free_image();
00118 av_free(ci->cache);
00119 }
00120 if (ctx) {
00121 if (ci->imageOverlaid) {
00122 imlib_context_set_image(ci->imageOverlaid);
00123 imlib_free_image();
00124 }
00125 ff_eval_free(ci->eval_x);
00126 ff_eval_free(ci->eval_y);
00127 ff_eval_free(ci->eval_r);
00128 ff_eval_free(ci->eval_g);
00129 ff_eval_free(ci->eval_b);
00130 ff_eval_free(ci->eval_a);
00131 
00132 av_free(ci->expr_x);
00133 av_free(ci->expr_y);
00134 av_free(ci->expr_R);
00135 av_free(ci->expr_G);
00136 av_free(ci->expr_B);
00137 av_free(ci->expr_A);
00138 sws_freeContext(ci->toRGB_convert_ctx);
00139 sws_freeContext(ci->fromRGB_convert_ctx);
00140 av_free(ctx);
00141 }
00142 }
00143 
00144 int Configure(void **ctxp, int argc, char *argv[])
00145 {
00146 int c;
00147 ContextInfo *ci;
00148 char *rgbtxt = 0;
00149 const char *font = "LucidaSansDemiBold/16";
00150 char *fp = getenv("FONTPATH");
00151 char *color = 0;
00152 FILE *f;
00153 char *p;
00154 const char *error;
00155 
00156 *ctxp = av_mallocz(sizeof(ContextInfo));
00157 ci = (ContextInfo *) *ctxp;
00158 
00159 ci->x = 0.0;
00160 ci->y = 0.0;
00161 ci->expr_x = "0.0";
00162 ci->expr_y = "0.0";
00163 
00164 optind = 1;
00165 
00166 /* Use ':' to split FONTPATH */
00167 if (fp)
00168 while ((p = strchr(fp, ':'))) {
00169 *p = 0;
00170 imlib_add_path_to_font_path(fp);
00171 fp = p + 1;
00172 }
00173 if ((fp) && (*fp))
00174 imlib_add_path_to_font_path(fp);
00175 
00176 
00177 while ((c = getopt(argc, argv, "R:G:B:A:C:c:f:F:t:x:y:i:")) > 0) {
00178 switch (c) {
00179 case 'R':
00180 ci->expr_R = av_strdup(optarg);
00181 ci->eval_colors = 1;
00182 break;
00183 case 'G':
00184 ci->expr_G = av_strdup(optarg);
00185 ci->eval_colors = 1;
00186 break;
00187 case 'B':
00188 ci->expr_B = av_strdup(optarg);
00189 ci->eval_colors = 1;
00190 break;
00191 case 'A':
00192 ci->expr_A = av_strdup(optarg);
00193 break;
00194 case 'C':
00195 rgbtxt = optarg;
00196 break;
00197 case 'c':
00198 color = optarg;
00199 break;
00200 case 'F':
00201 font = optarg;
00202 break;
00203 case 't':
00204 ci->text = av_strdup(optarg);
00205 break;
00206 case 'f':
00207 ci->file = av_strdup(optarg);
00208 break;
00209 case 'x':
00210 ci->expr_x = av_strdup(optarg);
00211 break;
00212 case 'y':
00213 ci->expr_y = av_strdup(optarg);
00214 break;
00215 case 'i':
00216 ci->fileImage = av_strdup(optarg);
00217 break;
00218 case '?':
00219 av_log(NULL, AV_LOG_ERROR, "Unrecognized argument '%s'\n", argv[optind]);
00220 return -1;
00221 }
00222 }
00223 
00224 if (ci->eval_colors && !(ci->expr_R && ci->expr_G && ci->expr_B))
00225 {
00226 av_log(NULL, AV_LOG_ERROR, "You must specify expressions for all or no colors.\n");
00227 return -1;
00228 }
00229 
00230 if (ci->text || ci->file) {
00231 ci->fn = imlib_load_font(font);
00232 if (!ci->fn) {
00233 av_log(NULL, AV_LOG_ERROR, "Failed to load font '%s'\n", font);
00234 return -1;
00235 }
00236 imlib_context_set_font(ci->fn);
00237 imlib_context_set_direction(IMLIB_TEXT_TO_RIGHT);
00238 }
00239 
00240 if (color) {
00241 char buff[256];
00242 int done = 0;
00243 
00244 if (ci->eval_colors)
00245 {
00246 av_log(NULL, AV_LOG_ERROR, "You must not specify both a color name and expressions for the colors.\n");
00247 return -1;
00248 }
00249 
00250 if (rgbtxt)
00251 f = fopen(rgbtxt, "r");
00252 else
00253 {
00254 f = fopen("/usr/share/X11/rgb.txt", "r");
00255 if (!f)
00256 f = fopen("/usr/lib/X11/rgb.txt", "r");
00257 }
00258 if (!f) {
00259 av_log(NULL, AV_LOG_ERROR, "Failed to find RGB color names file\n");
00260 return -1;
00261 }
00262 while (fgets(buff, sizeof(buff), f)) {
00263 int r, g, b;
00264 char colname[80];
00265 
00266 if (sscanf(buff, "%d %d %d %64s", &r, &g, &b, colname) == 4 &&
00267 strcasecmp(colname, color) == 0) {
00268 ci->r = r;
00269 ci->g = g;
00270 ci->b = b;
00271 /* fprintf(stderr, "%s -> %d,%d,%d\n", colname, r, g, b); */
00272 done = 1;
00273 break;
00274 }
00275 }
00276 fclose(f);
00277 if (!done) {
00278 av_log(NULL, AV_LOG_ERROR, "Unable to find color '%s' in rgb.txt\n", color);
00279 return -1;
00280 }
00281 } else if (ci->eval_colors) {
00282 if (!(ci->eval_r = ff_parse(ci->expr_R, const_names, NULL, NULL, NULL, NULL, &error))){
00283 av_log(NULL, AV_LOG_ERROR, "Couldn't parse R expression '%s': %s\n", ci->expr_R, error);
00284 return -1;
00285 }
00286 if (!(ci->eval_g = ff_parse(ci->expr_G, const_names, NULL, NULL, NULL, NULL, &error))){
00287 av_log(NULL, AV_LOG_ERROR, "Couldn't parse G expression '%s': %s\n", ci->expr_G, error);
00288 return -1;
00289 }
00290 if (!(ci->eval_b = ff_parse(ci->expr_B, const_names, NULL, NULL, NULL, NULL, &error))){
00291 av_log(NULL, AV_LOG_ERROR, "Couldn't parse B expression '%s': %s\n", ci->expr_B, error);
00292 return -1;
00293 }
00294 }
00295 
00296 if (ci->expr_A) {
00297 if (!(ci->eval_a = ff_parse(ci->expr_A, const_names, NULL, NULL, NULL, NULL, &error))){
00298 av_log(NULL, AV_LOG_ERROR, "Couldn't parse A expression '%s': %s\n", ci->expr_A, error);
00299 return -1;
00300 }
00301 } else {
00302 ci->a = 255;
00303 }
00304 
00305 if (!(ci->eval_colors || ci->eval_a))
00306 imlib_context_set_color(ci->r, ci->g, ci->b, ci->a);
00307 
00308 /* load the image (for example, credits for a movie) */
00309 if (ci->fileImage) {
00310 ci->imageOverlaid = imlib_load_image_immediately(ci->fileImage);
00311 if (!(ci->imageOverlaid)){
00312 av_log(NULL, AV_LOG_ERROR, "Couldn't load image '%s'\n", ci->fileImage);
00313 return -1;
00314 }
00315 imlib_context_set_image(ci->imageOverlaid);
00316 ci->imageOverlaid_width = imlib_image_get_width();
00317 ci->imageOverlaid_height = imlib_image_get_height();
00318 }
00319 
00320 if (!(ci->eval_x = ff_parse(ci->expr_x, const_names, NULL, NULL, NULL, NULL, &error))){
00321 av_log(NULL, AV_LOG_ERROR, "Couldn't parse x expression '%s': %s\n", ci->expr_x, error);
00322 return -1;
00323 }
00324 
00325 if (!(ci->eval_y = ff_parse(ci->expr_y, const_names, NULL, NULL, NULL, NULL, &error))){
00326 av_log(NULL, AV_LOG_ERROR, "Couldn't parse y expression '%s': %s\n", ci->expr_y, error);
00327 return -1;
00328 }
00329 
00330 return 0;
00331 }
00332 
00333 static Imlib_Image get_cached_image(ContextInfo *ci, int width, int height)
00334 {
00335 CachedImage *cache;
00336 
00337 for (cache = ci->cache; cache; cache = cache->next) {
00338 if (width == cache->width && height == cache->height)
00339 return cache->image;
00340 }
00341 
00342 return NULL;
00343 }
00344 
00345 static void put_cached_image(ContextInfo *ci, Imlib_Image image, int width, int height)
00346 {
00347 CachedImage *cache = av_mallocz(sizeof(*cache));
00348 
00349 cache->image = image;
00350 cache->width = width;
00351 cache->height = height;
00352 cache->next = ci->cache;
00353 ci->cache = cache;
00354 }
00355 
00356 void Process(void *ctx, AVPicture *picture, enum PixelFormat pix_fmt, int width, int height, int64_t pts)
00357 {
00358 ContextInfo *ci = (ContextInfo *) ctx;
00359 AVPicture picture1;
00360 Imlib_Image image;
00361 DATA32 *data;
00362 
00363 image = get_cached_image(ci, width, height);
00364 
00365 if (!image) {
00366 image = imlib_create_image(width, height);
00367 put_cached_image(ci, image, width, height);
00368 }
00369 
00370 imlib_context_set_image(image);
00371 data = imlib_image_get_data();
00372 
00373 avpicture_fill(&picture1, (uint8_t *) data, PIX_FMT_RGB32, width, height);
00374 
00375 // if we already got a SWS context, let's realloc if is not re-useable
00376 ci->toRGB_convert_ctx = sws_getCachedContext(ci->toRGB_convert_ctx,
00377 width, height, pix_fmt,
00378 width, height, PIX_FMT_RGB32,
00379 sws_flags, NULL, NULL, NULL);
00380 if (ci->toRGB_convert_ctx == NULL) {
00381 av_log(NULL, AV_LOG_ERROR,
00382 "Cannot initialize the toRGB conversion context\n");
00383 return;
00384 }
00385 
00386 // img_convert parameters are 2 first destination, then 4 source
00387 // sws_scale parameters are context, 4 first source, then 2 destination
00388 sws_scale(ci->toRGB_convert_ctx,
00389 picture->data, picture->linesize, 0, height,
00390 picture1.data, picture1.linesize);
00391 
00392 imlib_image_set_has_alpha(0);
00393 
00394 {
00395 int wid, hig, h_a, v_a;
00396 char buff[1000];
00397 char tbuff[1000];
00398 const char *tbp = ci->text;
00399 time_t now = time(0);
00400 char *p, *q;
00401 int y;
00402 
00403 double const_values[]={
00404 M_PI,
00405 M_E,
00406 ci->frame_number, // frame number (starting at zero)
00407 height, // frame height
00408 width, // frame width
00409 ci->imageOverlaid_height, // image height
00410 ci->imageOverlaid_width, // image width
00411 ci->x, // previous x
00412 ci->y, // previous y
00413 0
00414 };
00415 
00416 if (ci->file) {
00417 int fd = open(ci->file, O_RDONLY);
00418 
00419 if (fd < 0) {
00420 tbp = "[File not found]";
00421 } else {
00422 int l = read(fd, tbuff, sizeof(tbuff) - 1);
00423 
00424 if (l >= 0) {
00425 tbuff[l] = 0;
00426 tbp = tbuff;
00427 } else {
00428 tbp = "[I/O Error]";
00429 }
00430 close(fd);
00431 }
00432 }
00433 
00434 if (tbp)
00435 strftime(buff, sizeof(buff), tbp, localtime(&now));
00436 else if (!(ci->imageOverlaid))
00437 strftime(buff, sizeof(buff), "[No data]", localtime(&now));
00438 
00439 ci->x = ff_parse_eval(ci->eval_x, const_values, ci);
00440 ci->y = ff_parse_eval(ci->eval_y, const_values, ci);
00441 y = ci->y;
00442 
00443 if (ci->eval_a) {
00444 ci->a = ff_parse_eval(ci->eval_a, const_values, ci);
00445 }
00446 
00447 if (ci->eval_colors) {
00448 ci->r = ff_parse_eval(ci->eval_r, const_values, ci);
00449 ci->g = ff_parse_eval(ci->eval_g, const_values, ci);
00450 ci->b = ff_parse_eval(ci->eval_b, const_values, ci);
00451 }
00452 
00453 if (ci->eval_colors || ci->eval_a) {
00454 imlib_context_set_color(ci->r, ci->g, ci->b, ci->a);
00455 }
00456 
00457 if (!(ci->imageOverlaid))
00458 for (p = buff; p; p = q) {
00459 q = strchr(p, '\n');
00460 if (q)
00461 *q++ = 0;
00462 
00463 imlib_text_draw_with_return_metrics(ci->x, y, p, &wid, &hig, &h_a, &v_a);
00464 y += v_a;
00465 }
00466 
00467 if (ci->imageOverlaid) {
00468 imlib_context_set_image(image);
00469 imlib_blend_image_onto_image(ci->imageOverlaid, 0,
00470 0, 0, ci->imageOverlaid_width, ci->imageOverlaid_height,
00471 ci->x, ci->y, ci->imageOverlaid_width, ci->imageOverlaid_height);
00472 }
00473 
00474 }
00475 
00476 ci->fromRGB_convert_ctx = sws_getCachedContext(ci->fromRGB_convert_ctx,
00477 width, height, PIX_FMT_RGB32,
00478 width, height, pix_fmt,
00479 sws_flags, NULL, NULL, NULL);
00480 if (ci->fromRGB_convert_ctx == NULL) {
00481 av_log(NULL, AV_LOG_ERROR,
00482 "Cannot initialize the fromRGB conversion context\n");
00483 return;
00484 }
00485 // img_convert parameters are 2 first destination, then 4 source
00486 // sws_scale parameters are context, 4 first source, then 2 destination
00487 sws_scale(ci->fromRGB_convert_ctx,
00488 picture1.data, picture1.linesize, 0, height,
00489 picture->data, picture->linesize);
00490 
00491 ci->frame_number++;
00492 }
00493 

Generated on Fri Oct 26 02:35:42 2012 for FFmpeg by doxygen 1.5.8

AltStyle によって変換されたページ (->オリジナル) /