imlib2.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. /*
  2. * imlib2 based hook
  3. * Copyright (c) 2002 Philip Gladstone
  4. *
  5. * This module is very much intended as an example of what could be done.
  6. *
  7. * One caution is that this is an expensive process -- in particular the
  8. * conversion of the image into RGB and back is time consuming. For some
  9. * special cases -- e.g. painting black text -- it would be faster to paint
  10. * the text into a bitmap and then combine it directly into the YUV
  11. * image. However, this code is fast enough to handle 10 fps of 320x240 on a
  12. * 900MHz Duron in maybe 15% of the CPU.
  13. * See further statistics on Pentium4, 3GHz, FFMpeg is SVN-r6798
  14. * Input movie is 20.2 seconds of PAL DV on AVI
  15. * Output movie is DVD compliant VOB.
  16. *
  17. ffmpeg -i input.avi -target pal-dvd out.vob
  18. # 13.516s just transcode
  19. ffmpeg -i input.avi -vhook /usr/local/bin/vhook/null.dll -target pal-dvd out.vob
  20. # 23.546s transcode and img_convert
  21. ffmpeg -i input.avi -vhook \
  22. 'vhook/imlib2.dll -c red -F Vera/20 -x 150-0.5*N -y 70+0.25*N -t Hello_person' \
  23. -target pal-dvd out.vob
  24. # 21.454s transcode, img_convert and move text around
  25. ffmpeg -i input.avi -vhook \
  26. 'vhook/imlib2.dll -x 150-0.5*N -y 70+0.25*N -i /usr/share/imlib2/data/images/bulb.png' \
  27. -target pal-dvd out.vob
  28. # 20.828s transcode, img_convert and move image around
  29. *
  30. * This file is part of FFmpeg.
  31. *
  32. * FFmpeg is free software; you can redistribute it and/or
  33. * modify it under the terms of the GNU Lesser General Public
  34. * License as published by the Free Software Foundation; either
  35. * version 2.1 of the License, or (at your option) any later version.
  36. *
  37. * FFmpeg is distributed in the hope that it will be useful,
  38. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  39. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  40. * Lesser General Public License for more details.
  41. *
  42. * You should have received a copy of the GNU Lesser General Public
  43. * License along with FFmpeg; if not, write to the Free Software
  44. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  45. */
  46. #include "framehook.h"
  47. #include "swscale.h"
  48. #include <stdio.h>
  49. #include <stdlib.h>
  50. #include <fcntl.h>
  51. #include <stdarg.h>
  52. #include <string.h>
  53. #include <unistd.h>
  54. #undef time
  55. #include <sys/time.h>
  56. #include <time.h>
  57. #include <Imlib2.h>
  58. #include "eval.h"
  59. const char *const_names[]={
  60. "PI",
  61. "E",
  62. "N", // frame number (starting at zero)
  63. "H", // frame height
  64. "W", // frame width
  65. "h", // image height
  66. "w", // image width
  67. "X", // previous x
  68. "Y", // previous y
  69. NULL
  70. };
  71. static int sws_flags = SWS_BICUBIC;
  72. typedef struct {
  73. int dummy;
  74. Imlib_Font fn;
  75. char *text;
  76. char *file;
  77. int r, g, b, a;
  78. AVEvalExpr *eval_r, *eval_g, *eval_b, *eval_a;
  79. char *expr_R, *expr_G, *expr_B, *expr_A;
  80. int eval_colors;
  81. double x, y;
  82. char *fileImage;
  83. struct CachedImage *cache;
  84. Imlib_Image imageOverlaid;
  85. AVEvalExpr *eval_x, *eval_y;
  86. char *expr_x, *expr_y;
  87. int frame_number;
  88. int imageOverlaid_width, imageOverlaid_height;
  89. // This vhook first converts frame to RGB ...
  90. struct SwsContext *toRGB_convert_ctx;
  91. // ... and then converts back frame from RGB to initial format
  92. struct SwsContext *fromRGB_convert_ctx;
  93. } ContextInfo;
  94. typedef struct CachedImage {
  95. struct CachedImage *next;
  96. Imlib_Image image;
  97. int width;
  98. int height;
  99. } CachedImage;
  100. void Release(void *ctx)
  101. {
  102. ContextInfo *ci;
  103. ci = (ContextInfo *) ctx;
  104. if (ci->cache) {
  105. imlib_context_set_image(ci->cache->image);
  106. imlib_free_image();
  107. av_free(ci->cache);
  108. }
  109. if (ctx) {
  110. if (ci->imageOverlaid) {
  111. imlib_context_set_image(ci->imageOverlaid);
  112. imlib_free_image();
  113. }
  114. ff_eval_free(ci->eval_x);
  115. ff_eval_free(ci->eval_y);
  116. ff_eval_free(ci->eval_r);
  117. ff_eval_free(ci->eval_g);
  118. ff_eval_free(ci->eval_b);
  119. ff_eval_free(ci->eval_a);
  120. av_free(ci->expr_x);
  121. av_free(ci->expr_y);
  122. av_free(ci->expr_R);
  123. av_free(ci->expr_G);
  124. av_free(ci->expr_B);
  125. av_free(ci->expr_A);
  126. sws_freeContext(ci->toRGB_convert_ctx);
  127. sws_freeContext(ci->fromRGB_convert_ctx);
  128. av_free(ctx);
  129. }
  130. }
  131. int Configure(void **ctxp, int argc, char *argv[])
  132. {
  133. int c;
  134. ContextInfo *ci;
  135. char *rgbtxt = 0;
  136. char *font = "LucidaSansDemiBold/16";
  137. char *fp = getenv("FONTPATH");
  138. char *color = 0;
  139. FILE *f;
  140. char *p;
  141. char *error;
  142. *ctxp = av_mallocz(sizeof(ContextInfo));
  143. ci = (ContextInfo *) *ctxp;
  144. ci->x = 0.0;
  145. ci->y = 0.0;
  146. ci->expr_x = "0.0";
  147. ci->expr_y = "0.0";
  148. optind = 1;
  149. /* Use ':' to split FONTPATH */
  150. if (fp)
  151. while (p = strchr(fp, ':')) {
  152. *p = 0;
  153. imlib_add_path_to_font_path(fp);
  154. fp = p + 1;
  155. }
  156. if ((fp) && (*fp))
  157. imlib_add_path_to_font_path(fp);
  158. while ((c = getopt(argc, argv, "R:G:B:A:C:c:f:F:t:x:y:i:")) > 0) {
  159. switch (c) {
  160. case 'R':
  161. ci->expr_R = av_strdup(optarg);
  162. ci->eval_colors = 1;
  163. break;
  164. case 'G':
  165. ci->expr_G = av_strdup(optarg);
  166. ci->eval_colors = 1;
  167. break;
  168. case 'B':
  169. ci->expr_B = av_strdup(optarg);
  170. ci->eval_colors = 1;
  171. break;
  172. case 'A':
  173. ci->expr_A = av_strdup(optarg);
  174. break;
  175. case 'C':
  176. rgbtxt = optarg;
  177. break;
  178. case 'c':
  179. color = optarg;
  180. break;
  181. case 'F':
  182. font = optarg;
  183. break;
  184. case 't':
  185. ci->text = av_strdup(optarg);
  186. break;
  187. case 'f':
  188. ci->file = av_strdup(optarg);
  189. break;
  190. case 'x':
  191. ci->expr_x = av_strdup(optarg);
  192. break;
  193. case 'y':
  194. ci->expr_y = av_strdup(optarg);
  195. break;
  196. case 'i':
  197. ci->fileImage = av_strdup(optarg);
  198. break;
  199. case '?':
  200. fprintf(stderr, "Unrecognized argument '%s'\n", argv[optind]);
  201. return -1;
  202. }
  203. }
  204. if (ci->eval_colors && !(ci->expr_R && ci->expr_G && ci->expr_B))
  205. {
  206. fprintf(stderr, "You must specify expressions for all or no colors.\n");
  207. return -1;
  208. }
  209. if (ci->text || ci->file) {
  210. ci->fn = imlib_load_font(font);
  211. if (!ci->fn) {
  212. fprintf(stderr, "Failed to load font '%s'\n", font);
  213. return -1;
  214. }
  215. imlib_context_set_font(ci->fn);
  216. imlib_context_set_direction(IMLIB_TEXT_TO_RIGHT);
  217. }
  218. if (color) {
  219. char buff[256];
  220. int done = 0;
  221. if (ci->eval_colors)
  222. {
  223. fprintf(stderr, "You must not specify both a color name and expressions for the colors.\n");
  224. return -1;
  225. }
  226. if (rgbtxt)
  227. f = fopen(rgbtxt, "r");
  228. else
  229. {
  230. f = fopen("/usr/share/X11/rgb.txt", "r");
  231. if (!f)
  232. f = fopen("/usr/lib/X11/rgb.txt", "r");
  233. }
  234. if (!f) {
  235. fprintf(stderr, "Failed to find RGB color names file\n");
  236. return -1;
  237. }
  238. while (fgets(buff, sizeof(buff), f)) {
  239. int r, g, b;
  240. char colname[80];
  241. if (sscanf(buff, "%d %d %d %64s", &r, &g, &b, colname) == 4 &&
  242. strcasecmp(colname, color) == 0) {
  243. ci->r = r;
  244. ci->g = g;
  245. ci->b = b;
  246. /* fprintf(stderr, "%s -> %d,%d,%d\n", colname, r, g, b); */
  247. done = 1;
  248. break;
  249. }
  250. }
  251. fclose(f);
  252. if (!done) {
  253. fprintf(stderr, "Unable to find color '%s' in rgb.txt\n", color);
  254. return -1;
  255. }
  256. } else if (ci->eval_colors) {
  257. if (!(ci->eval_r = ff_parse(ci->expr_R, const_names, NULL, NULL, NULL, NULL, &error))){
  258. av_log(NULL, AV_LOG_ERROR, "Couldn't parse R expression '%s': %s\n", ci->expr_R, error);
  259. return -1;
  260. }
  261. if (!(ci->eval_g = ff_parse(ci->expr_G, const_names, NULL, NULL, NULL, NULL, &error))){
  262. av_log(NULL, AV_LOG_ERROR, "Couldn't parse G expression '%s': %s\n", ci->expr_G, error);
  263. return -1;
  264. }
  265. if (!(ci->eval_b = ff_parse(ci->expr_B, const_names, NULL, NULL, NULL, NULL, &error))){
  266. av_log(NULL, AV_LOG_ERROR, "Couldn't parse B expression '%s': %s\n", ci->expr_B, error);
  267. return -1;
  268. }
  269. }
  270. if (ci->expr_A) {
  271. if (!(ci->eval_a = ff_parse(ci->expr_A, const_names, NULL, NULL, NULL, NULL, &error))){
  272. av_log(NULL, AV_LOG_ERROR, "Couldn't parse A expression '%s': %s\n", ci->expr_A, error);
  273. return -1;
  274. }
  275. } else {
  276. ci->a = 255;
  277. }
  278. if (!(ci->eval_colors || ci->eval_a))
  279. imlib_context_set_color(ci->r, ci->g, ci->b, ci->a);
  280. /* load the image (for example, credits for a movie) */
  281. if (ci->fileImage) {
  282. ci->imageOverlaid = imlib_load_image_immediately(ci->fileImage);
  283. if (!(ci->imageOverlaid)){
  284. av_log(NULL, AV_LOG_ERROR, "Couldn't load image '%s'\n", ci->fileImage);
  285. return -1;
  286. }
  287. imlib_context_set_image(ci->imageOverlaid);
  288. ci->imageOverlaid_width = imlib_image_get_width();
  289. ci->imageOverlaid_height = imlib_image_get_height();
  290. }
  291. if (!(ci->eval_x = ff_parse(ci->expr_x, const_names, NULL, NULL, NULL, NULL, &error))){
  292. av_log(NULL, AV_LOG_ERROR, "Couldn't parse x expression '%s': %s\n", ci->expr_x, error);
  293. return -1;
  294. }
  295. if (!(ci->eval_y = ff_parse(ci->expr_y, const_names, NULL, NULL, NULL, NULL, &error))){
  296. av_log(NULL, AV_LOG_ERROR, "Couldn't parse y expression '%s': %s\n", ci->expr_y, error);
  297. return -1;
  298. }
  299. return 0;
  300. }
  301. static Imlib_Image get_cached_image(ContextInfo *ci, int width, int height)
  302. {
  303. CachedImage *cache;
  304. for (cache = ci->cache; cache; cache = cache->next) {
  305. if (width == cache->width && height == cache->height)
  306. return cache->image;
  307. }
  308. return NULL;
  309. }
  310. static void put_cached_image(ContextInfo *ci, Imlib_Image image, int width, int height)
  311. {
  312. CachedImage *cache = av_mallocz(sizeof(*cache));
  313. cache->image = image;
  314. cache->width = width;
  315. cache->height = height;
  316. cache->next = ci->cache;
  317. ci->cache = cache;
  318. }
  319. void Process(void *ctx, AVPicture *picture, enum PixelFormat pix_fmt, int width, int height, int64_t pts)
  320. {
  321. ContextInfo *ci = (ContextInfo *) ctx;
  322. AVPicture picture1;
  323. Imlib_Image image;
  324. DATA32 *data;
  325. image = get_cached_image(ci, width, height);
  326. if (!image) {
  327. image = imlib_create_image(width, height);
  328. put_cached_image(ci, image, width, height);
  329. }
  330. imlib_context_set_image(image);
  331. data = imlib_image_get_data();
  332. avpicture_fill(&picture1, (uint8_t *) data, PIX_FMT_RGB32, width, height);
  333. // if we already got a SWS context, let's realloc if is not re-useable
  334. ci->toRGB_convert_ctx = sws_getCachedContext(ci->toRGB_convert_ctx,
  335. width, height, pix_fmt,
  336. width, height, PIX_FMT_RGB32,
  337. sws_flags, NULL, NULL, NULL);
  338. if (ci->toRGB_convert_ctx == NULL) {
  339. av_log(NULL, AV_LOG_ERROR,
  340. "Cannot initialize the toRGB conversion context\n");
  341. return;
  342. }
  343. // img_convert parameters are 2 first destination, then 4 source
  344. // sws_scale parameters are context, 4 first source, then 2 destination
  345. sws_scale(ci->toRGB_convert_ctx,
  346. picture->data, picture->linesize, 0, height,
  347. picture1.data, picture1.linesize);
  348. imlib_image_set_has_alpha(0);
  349. {
  350. int wid, hig, h_a, v_a;
  351. char buff[1000];
  352. char tbuff[1000];
  353. char *tbp = ci->text;
  354. time_t now = time(0);
  355. char *p, *q;
  356. int y;
  357. double const_values[]={
  358. M_PI,
  359. M_E,
  360. ci->frame_number, // frame number (starting at zero)
  361. height, // frame height
  362. width, // frame width
  363. ci->imageOverlaid_height, // image height
  364. ci->imageOverlaid_width, // image width
  365. ci->x, // previous x
  366. ci->y, // previous y
  367. 0
  368. };
  369. if (ci->file) {
  370. int fd = open(ci->file, O_RDONLY);
  371. if (fd < 0) {
  372. tbp = "[File not found]";
  373. } else {
  374. int l = read(fd, tbuff, sizeof(tbuff) - 1);
  375. if (l >= 0) {
  376. tbuff[l] = 0;
  377. tbp = tbuff;
  378. } else {
  379. tbp = "[I/O Error]";
  380. }
  381. close(fd);
  382. }
  383. }
  384. if (tbp)
  385. strftime(buff, sizeof(buff), tbp, localtime(&now));
  386. else if (!(ci->imageOverlaid))
  387. strftime(buff, sizeof(buff), "[No data]", localtime(&now));
  388. ci->x = ff_parse_eval(ci->eval_x, const_values, ci);
  389. ci->y = ff_parse_eval(ci->eval_y, const_values, ci);
  390. y = ci->y;
  391. if (ci->eval_a) {
  392. ci->a = ff_parse_eval(ci->eval_a, const_values, ci);
  393. }
  394. if (ci->eval_colors) {
  395. ci->r = ff_parse_eval(ci->eval_r, const_values, ci);
  396. ci->g = ff_parse_eval(ci->eval_g, const_values, ci);
  397. ci->b = ff_parse_eval(ci->eval_b, const_values, ci);
  398. }
  399. if (ci->eval_colors || ci->eval_a) {
  400. imlib_context_set_color(ci->r, ci->g, ci->b, ci->a);
  401. }
  402. if (!(ci->imageOverlaid))
  403. for (p = buff; p; p = q) {
  404. q = strchr(p, '\n');
  405. if (q)
  406. *q++ = 0;
  407. imlib_text_draw_with_return_metrics(ci->x, y, p, &wid, &hig, &h_a, &v_a);
  408. y += v_a;
  409. }
  410. if (ci->imageOverlaid) {
  411. imlib_context_set_image(image);
  412. imlib_blend_image_onto_image(ci->imageOverlaid, 0,
  413. 0, 0, ci->imageOverlaid_width, ci->imageOverlaid_height,
  414. ci->x, ci->y, ci->imageOverlaid_width, ci->imageOverlaid_height);
  415. }
  416. }
  417. ci->fromRGB_convert_ctx = sws_getCachedContext(ci->fromRGB_convert_ctx,
  418. width, height, PIX_FMT_RGB32,
  419. width, height, pix_fmt,
  420. sws_flags, NULL, NULL, NULL);
  421. if (ci->fromRGB_convert_ctx == NULL) {
  422. av_log(NULL, AV_LOG_ERROR,
  423. "Cannot initialize the fromRGB conversion context\n");
  424. return;
  425. }
  426. // img_convert parameters are 2 first destination, then 4 source
  427. // sws_scale parameters are context, 4 first source, then 2 destination
  428. sws_scale(ci->fromRGB_convert_ctx,
  429. picture1.data, picture1.linesize, 0, height,
  430. picture->data, picture->linesize);
  431. ci->frame_number++;
  432. }