imlib2.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  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 "libavformat/framehook.h"
  47. #include "libswscale/swscale.h"
  48. #include <stdio.h>
  49. #include <stdlib.h>
  50. #include <fcntl.h>
  51. #include <stdarg.h>
  52. #include <string.h>
  53. #include <strings.h>
  54. #include <unistd.h>
  55. #undef time
  56. #include <sys/time.h>
  57. #include <time.h>
  58. #include <Imlib2.h>
  59. #include "libavcodec/eval.h"
  60. const char *const_names[]={
  61. "PI",
  62. "E",
  63. "N", // frame number (starting at zero)
  64. "H", // frame height
  65. "W", // frame width
  66. "h", // image height
  67. "w", // image width
  68. "X", // previous x
  69. "Y", // previous y
  70. NULL
  71. };
  72. static int sws_flags = SWS_BICUBIC;
  73. typedef struct {
  74. int dummy;
  75. Imlib_Font fn;
  76. char *text;
  77. char *file;
  78. int r, g, b, a;
  79. AVEvalExpr *eval_r, *eval_g, *eval_b, *eval_a;
  80. char *expr_R, *expr_G, *expr_B, *expr_A;
  81. int eval_colors;
  82. double x, y;
  83. char *fileImage;
  84. struct CachedImage *cache;
  85. Imlib_Image imageOverlaid;
  86. AVEvalExpr *eval_x, *eval_y;
  87. char *expr_x, *expr_y;
  88. int frame_number;
  89. int imageOverlaid_width, imageOverlaid_height;
  90. // This vhook first converts frame to RGB ...
  91. struct SwsContext *toRGB_convert_ctx;
  92. // ... and then converts back frame from RGB to initial format
  93. struct SwsContext *fromRGB_convert_ctx;
  94. } ContextInfo;
  95. typedef struct CachedImage {
  96. struct CachedImage *next;
  97. Imlib_Image image;
  98. int width;
  99. int height;
  100. } CachedImage;
  101. void Release(void *ctx)
  102. {
  103. ContextInfo *ci;
  104. ci = (ContextInfo *) ctx;
  105. if (ci->cache) {
  106. imlib_context_set_image(ci->cache->image);
  107. imlib_free_image();
  108. av_free(ci->cache);
  109. }
  110. if (ctx) {
  111. if (ci->imageOverlaid) {
  112. imlib_context_set_image(ci->imageOverlaid);
  113. imlib_free_image();
  114. }
  115. ff_eval_free(ci->eval_x);
  116. ff_eval_free(ci->eval_y);
  117. ff_eval_free(ci->eval_r);
  118. ff_eval_free(ci->eval_g);
  119. ff_eval_free(ci->eval_b);
  120. ff_eval_free(ci->eval_a);
  121. av_free(ci->expr_x);
  122. av_free(ci->expr_y);
  123. av_free(ci->expr_R);
  124. av_free(ci->expr_G);
  125. av_free(ci->expr_B);
  126. av_free(ci->expr_A);
  127. sws_freeContext(ci->toRGB_convert_ctx);
  128. sws_freeContext(ci->fromRGB_convert_ctx);
  129. av_free(ctx);
  130. }
  131. }
  132. int Configure(void **ctxp, int argc, char *argv[])
  133. {
  134. int c;
  135. ContextInfo *ci;
  136. char *rgbtxt = 0;
  137. char *font = "LucidaSansDemiBold/16";
  138. char *fp = getenv("FONTPATH");
  139. char *color = 0;
  140. FILE *f;
  141. char *p;
  142. char *error;
  143. *ctxp = av_mallocz(sizeof(ContextInfo));
  144. ci = (ContextInfo *) *ctxp;
  145. ci->x = 0.0;
  146. ci->y = 0.0;
  147. ci->expr_x = "0.0";
  148. ci->expr_y = "0.0";
  149. optind = 1;
  150. /* Use ':' to split FONTPATH */
  151. if (fp)
  152. while (p = strchr(fp, ':')) {
  153. *p = 0;
  154. imlib_add_path_to_font_path(fp);
  155. fp = p + 1;
  156. }
  157. if ((fp) && (*fp))
  158. imlib_add_path_to_font_path(fp);
  159. while ((c = getopt(argc, argv, "R:G:B:A:C:c:f:F:t:x:y:i:")) > 0) {
  160. switch (c) {
  161. case 'R':
  162. ci->expr_R = av_strdup(optarg);
  163. ci->eval_colors = 1;
  164. break;
  165. case 'G':
  166. ci->expr_G = av_strdup(optarg);
  167. ci->eval_colors = 1;
  168. break;
  169. case 'B':
  170. ci->expr_B = av_strdup(optarg);
  171. ci->eval_colors = 1;
  172. break;
  173. case 'A':
  174. ci->expr_A = av_strdup(optarg);
  175. break;
  176. case 'C':
  177. rgbtxt = optarg;
  178. break;
  179. case 'c':
  180. color = optarg;
  181. break;
  182. case 'F':
  183. font = optarg;
  184. break;
  185. case 't':
  186. ci->text = av_strdup(optarg);
  187. break;
  188. case 'f':
  189. ci->file = av_strdup(optarg);
  190. break;
  191. case 'x':
  192. ci->expr_x = av_strdup(optarg);
  193. break;
  194. case 'y':
  195. ci->expr_y = av_strdup(optarg);
  196. break;
  197. case 'i':
  198. ci->fileImage = av_strdup(optarg);
  199. break;
  200. case '?':
  201. av_log(NULL, AV_LOG_ERROR, "Unrecognized argument '%s'\n", argv[optind]);
  202. return -1;
  203. }
  204. }
  205. if (ci->eval_colors && !(ci->expr_R && ci->expr_G && ci->expr_B))
  206. {
  207. av_log(NULL, AV_LOG_ERROR, "You must specify expressions for all or no colors.\n");
  208. return -1;
  209. }
  210. if (ci->text || ci->file) {
  211. ci->fn = imlib_load_font(font);
  212. if (!ci->fn) {
  213. av_log(NULL, AV_LOG_ERROR, "Failed to load font '%s'\n", font);
  214. return -1;
  215. }
  216. imlib_context_set_font(ci->fn);
  217. imlib_context_set_direction(IMLIB_TEXT_TO_RIGHT);
  218. }
  219. if (color) {
  220. char buff[256];
  221. int done = 0;
  222. if (ci->eval_colors)
  223. {
  224. av_log(NULL, AV_LOG_ERROR, "You must not specify both a color name and expressions for the colors.\n");
  225. return -1;
  226. }
  227. if (rgbtxt)
  228. f = fopen(rgbtxt, "r");
  229. else
  230. {
  231. f = fopen("/usr/share/X11/rgb.txt", "r");
  232. if (!f)
  233. f = fopen("/usr/lib/X11/rgb.txt", "r");
  234. }
  235. if (!f) {
  236. av_log(NULL, AV_LOG_ERROR, "Failed to find RGB color names file\n");
  237. return -1;
  238. }
  239. while (fgets(buff, sizeof(buff), f)) {
  240. int r, g, b;
  241. char colname[80];
  242. if (sscanf(buff, "%d %d %d %64s", &r, &g, &b, colname) == 4 &&
  243. strcasecmp(colname, color) == 0) {
  244. ci->r = r;
  245. ci->g = g;
  246. ci->b = b;
  247. /* fprintf(stderr, "%s -> %d,%d,%d\n", colname, r, g, b); */
  248. done = 1;
  249. break;
  250. }
  251. }
  252. fclose(f);
  253. if (!done) {
  254. av_log(NULL, AV_LOG_ERROR, "Unable to find color '%s' in rgb.txt\n", color);
  255. return -1;
  256. }
  257. } else if (ci->eval_colors) {
  258. if (!(ci->eval_r = ff_parse(ci->expr_R, const_names, NULL, NULL, NULL, NULL, &error))){
  259. av_log(NULL, AV_LOG_ERROR, "Couldn't parse R expression '%s': %s\n", ci->expr_R, error);
  260. return -1;
  261. }
  262. if (!(ci->eval_g = ff_parse(ci->expr_G, const_names, NULL, NULL, NULL, NULL, &error))){
  263. av_log(NULL, AV_LOG_ERROR, "Couldn't parse G expression '%s': %s\n", ci->expr_G, error);
  264. return -1;
  265. }
  266. if (!(ci->eval_b = ff_parse(ci->expr_B, const_names, NULL, NULL, NULL, NULL, &error))){
  267. av_log(NULL, AV_LOG_ERROR, "Couldn't parse B expression '%s': %s\n", ci->expr_B, error);
  268. return -1;
  269. }
  270. }
  271. if (ci->expr_A) {
  272. if (!(ci->eval_a = ff_parse(ci->expr_A, const_names, NULL, NULL, NULL, NULL, &error))){
  273. av_log(NULL, AV_LOG_ERROR, "Couldn't parse A expression '%s': %s\n", ci->expr_A, error);
  274. return -1;
  275. }
  276. } else {
  277. ci->a = 255;
  278. }
  279. if (!(ci->eval_colors || ci->eval_a))
  280. imlib_context_set_color(ci->r, ci->g, ci->b, ci->a);
  281. /* load the image (for example, credits for a movie) */
  282. if (ci->fileImage) {
  283. ci->imageOverlaid = imlib_load_image_immediately(ci->fileImage);
  284. if (!(ci->imageOverlaid)){
  285. av_log(NULL, AV_LOG_ERROR, "Couldn't load image '%s'\n", ci->fileImage);
  286. return -1;
  287. }
  288. imlib_context_set_image(ci->imageOverlaid);
  289. ci->imageOverlaid_width = imlib_image_get_width();
  290. ci->imageOverlaid_height = imlib_image_get_height();
  291. }
  292. if (!(ci->eval_x = ff_parse(ci->expr_x, const_names, NULL, NULL, NULL, NULL, &error))){
  293. av_log(NULL, AV_LOG_ERROR, "Couldn't parse x expression '%s': %s\n", ci->expr_x, error);
  294. return -1;
  295. }
  296. if (!(ci->eval_y = ff_parse(ci->expr_y, const_names, NULL, NULL, NULL, NULL, &error))){
  297. av_log(NULL, AV_LOG_ERROR, "Couldn't parse y expression '%s': %s\n", ci->expr_y, error);
  298. return -1;
  299. }
  300. return 0;
  301. }
  302. static Imlib_Image get_cached_image(ContextInfo *ci, int width, int height)
  303. {
  304. CachedImage *cache;
  305. for (cache = ci->cache; cache; cache = cache->next) {
  306. if (width == cache->width && height == cache->height)
  307. return cache->image;
  308. }
  309. return NULL;
  310. }
  311. static void put_cached_image(ContextInfo *ci, Imlib_Image image, int width, int height)
  312. {
  313. CachedImage *cache = av_mallocz(sizeof(*cache));
  314. cache->image = image;
  315. cache->width = width;
  316. cache->height = height;
  317. cache->next = ci->cache;
  318. ci->cache = cache;
  319. }
  320. void Process(void *ctx, AVPicture *picture, enum PixelFormat pix_fmt, int width, int height, int64_t pts)
  321. {
  322. ContextInfo *ci = (ContextInfo *) ctx;
  323. AVPicture picture1;
  324. Imlib_Image image;
  325. DATA32 *data;
  326. image = get_cached_image(ci, width, height);
  327. if (!image) {
  328. image = imlib_create_image(width, height);
  329. put_cached_image(ci, image, width, height);
  330. }
  331. imlib_context_set_image(image);
  332. data = imlib_image_get_data();
  333. avpicture_fill(&picture1, (uint8_t *) data, PIX_FMT_RGB32, width, height);
  334. // if we already got a SWS context, let's realloc if is not re-useable
  335. ci->toRGB_convert_ctx = sws_getCachedContext(ci->toRGB_convert_ctx,
  336. width, height, pix_fmt,
  337. width, height, PIX_FMT_RGB32,
  338. sws_flags, NULL, NULL, NULL);
  339. if (ci->toRGB_convert_ctx == NULL) {
  340. av_log(NULL, AV_LOG_ERROR,
  341. "Cannot initialize the toRGB conversion context\n");
  342. return;
  343. }
  344. // img_convert parameters are 2 first destination, then 4 source
  345. // sws_scale parameters are context, 4 first source, then 2 destination
  346. sws_scale(ci->toRGB_convert_ctx,
  347. picture->data, picture->linesize, 0, height,
  348. picture1.data, picture1.linesize);
  349. imlib_image_set_has_alpha(0);
  350. {
  351. int wid, hig, h_a, v_a;
  352. char buff[1000];
  353. char tbuff[1000];
  354. char *tbp = ci->text;
  355. time_t now = time(0);
  356. char *p, *q;
  357. int y;
  358. double const_values[]={
  359. M_PI,
  360. M_E,
  361. ci->frame_number, // frame number (starting at zero)
  362. height, // frame height
  363. width, // frame width
  364. ci->imageOverlaid_height, // image height
  365. ci->imageOverlaid_width, // image width
  366. ci->x, // previous x
  367. ci->y, // previous y
  368. 0
  369. };
  370. if (ci->file) {
  371. int fd = open(ci->file, O_RDONLY);
  372. if (fd < 0) {
  373. tbp = "[File not found]";
  374. } else {
  375. int l = read(fd, tbuff, sizeof(tbuff) - 1);
  376. if (l >= 0) {
  377. tbuff[l] = 0;
  378. tbp = tbuff;
  379. } else {
  380. tbp = "[I/O Error]";
  381. }
  382. close(fd);
  383. }
  384. }
  385. if (tbp)
  386. strftime(buff, sizeof(buff), tbp, localtime(&now));
  387. else if (!(ci->imageOverlaid))
  388. strftime(buff, sizeof(buff), "[No data]", localtime(&now));
  389. ci->x = ff_parse_eval(ci->eval_x, const_values, ci);
  390. ci->y = ff_parse_eval(ci->eval_y, const_values, ci);
  391. y = ci->y;
  392. if (ci->eval_a) {
  393. ci->a = ff_parse_eval(ci->eval_a, const_values, ci);
  394. }
  395. if (ci->eval_colors) {
  396. ci->r = ff_parse_eval(ci->eval_r, const_values, ci);
  397. ci->g = ff_parse_eval(ci->eval_g, const_values, ci);
  398. ci->b = ff_parse_eval(ci->eval_b, const_values, ci);
  399. }
  400. if (ci->eval_colors || ci->eval_a) {
  401. imlib_context_set_color(ci->r, ci->g, ci->b, ci->a);
  402. }
  403. if (!(ci->imageOverlaid))
  404. for (p = buff; p; p = q) {
  405. q = strchr(p, '\n');
  406. if (q)
  407. *q++ = 0;
  408. imlib_text_draw_with_return_metrics(ci->x, y, p, &wid, &hig, &h_a, &v_a);
  409. y += v_a;
  410. }
  411. if (ci->imageOverlaid) {
  412. imlib_context_set_image(image);
  413. imlib_blend_image_onto_image(ci->imageOverlaid, 0,
  414. 0, 0, ci->imageOverlaid_width, ci->imageOverlaid_height,
  415. ci->x, ci->y, ci->imageOverlaid_width, ci->imageOverlaid_height);
  416. }
  417. }
  418. ci->fromRGB_convert_ctx = sws_getCachedContext(ci->fromRGB_convert_ctx,
  419. width, height, PIX_FMT_RGB32,
  420. width, height, pix_fmt,
  421. sws_flags, NULL, NULL, NULL);
  422. if (ci->fromRGB_convert_ctx == NULL) {
  423. av_log(NULL, AV_LOG_ERROR,
  424. "Cannot initialize the fromRGB conversion context\n");
  425. return;
  426. }
  427. // img_convert parameters are 2 first destination, then 4 source
  428. // sws_scale parameters are context, 4 first source, then 2 destination
  429. sws_scale(ci->fromRGB_convert_ctx,
  430. picture1.data, picture1.linesize, 0, height,
  431. picture->data, picture->linesize);
  432. ci->frame_number++;
  433. }