3 * Copyright (c) 2002 Philip Gladstone
5 * This module is very much intended as an example of what could be done.
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.
14 * See further statistics on Pentium4, 3GHz, FFMpeg is SVN-r6798
15 * Input movie is 20.2 seconds of PAL DV on AVI
16 * Output movie is DVD compliant VOB.
18 ffmpeg -i input.avi -target pal-dvd out.vob
19 # 13.516s just transcode
20 ffmpeg -i input.avi -vhook /usr/local/bin/vhook/null.dll -target pal-dvd out.vob
21 # 23.546s transcode and img_convert
22 ffmpeg -i input.avi -vhook \
23 'vhook/imlib2.dll -c red -F Vera/20 -x 150-0.5*N -y 70+0.25*N -t Hello_person' \
24 -target pal-dvd out.vob
25 # 21.454s transcode, img_convert and move text around
26 ffmpeg -i input.avi -vhook \
27 'vhook/imlib2.dll -x 150-0.5*N -y 70+0.25*N -i /usr/share/imlib2/data/images/bulb.png' \
28 -target pal-dvd out.vob
29 # 20.828s transcode, img_convert and move image around
31 * This file is part of FFmpeg.
33 * FFmpeg is free software; you can redistribute it and/or
34 * modify it under the terms of the GNU Lesser General Public
35 * License as published by the Free Software Foundation; either
36 * version 2.1 of the License, or (at your option) any later version.
38 * FFmpeg is distributed in the hope that it will be useful,
39 * but WITHOUT ANY WARRANTY; without even the implied warranty of
40 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
41 * Lesser General Public License for more details.
43 * You should have received a copy of the GNU Lesser General Public
44 * License along with FFmpeg; if not, write to the Free Software
45 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
48 #include "libavformat/framehook.h"
49 #include "libswscale/swscale.h"
62 #include "libavcodec/eval.h"
64 const char *const_names
[]={
67 "N", // frame number (starting at zero)
77 static int sws_flags
= SWS_BICUBIC
;
85 AVEvalExpr
*eval_r
, *eval_g
, *eval_b
, *eval_a
;
86 char *expr_R
, *expr_G
, *expr_B
, *expr_A
;
90 struct CachedImage
*cache
;
91 Imlib_Image imageOverlaid
;
92 AVEvalExpr
*eval_x
, *eval_y
;
93 char *expr_x
, *expr_y
;
95 int imageOverlaid_width
, imageOverlaid_height
;
97 // This vhook first converts frame to RGB ...
98 struct SwsContext
*toRGB_convert_ctx
;
99 // ... and then converts back frame from RGB to initial format
100 struct SwsContext
*fromRGB_convert_ctx
;
103 typedef struct CachedImage
{
104 struct CachedImage
*next
;
110 void Release(void *ctx
)
113 ci
= (ContextInfo
*) ctx
;
116 imlib_context_set_image(ci
->cache
->image
);
121 if (ci
->imageOverlaid
) {
122 imlib_context_set_image(ci
->imageOverlaid
);
125 ff_eval_free(ci
->eval_x
);
126 ff_eval_free(ci
->eval_y
);
127 ff_eval_free(ci
->eval_r
);
128 ff_eval_free(ci
->eval_g
);
129 ff_eval_free(ci
->eval_b
);
130 ff_eval_free(ci
->eval_a
);
138 sws_freeContext(ci
->toRGB_convert_ctx
);
139 sws_freeContext(ci
->fromRGB_convert_ctx
);
144 int Configure(void **ctxp
, int argc
, char *argv
[])
149 char *font
= "LucidaSansDemiBold/16";
150 char *fp
= getenv("FONTPATH");
156 *ctxp
= av_mallocz(sizeof(ContextInfo
));
157 ci
= (ContextInfo
*) *ctxp
;
166 /* Use ':' to split FONTPATH */
168 while (p
= strchr(fp
, ':')) {
170 imlib_add_path_to_font_path(fp
);
174 imlib_add_path_to_font_path(fp
);
177 while ((c
= getopt(argc
, argv
, "R:G:B:A:C:c:f:F:t:x:y:i:")) > 0) {
180 ci
->expr_R
= av_strdup(optarg
);
184 ci
->expr_G
= av_strdup(optarg
);
188 ci
->expr_B
= av_strdup(optarg
);
192 ci
->expr_A
= av_strdup(optarg
);
204 ci
->text
= av_strdup(optarg
);
207 ci
->file
= av_strdup(optarg
);
210 ci
->expr_x
= av_strdup(optarg
);
213 ci
->expr_y
= av_strdup(optarg
);
216 ci
->fileImage
= av_strdup(optarg
);
219 av_log(NULL
, AV_LOG_ERROR
, "Unrecognized argument '%s'\n", argv
[optind
]);
224 if (ci
->eval_colors
&& !(ci
->expr_R
&& ci
->expr_G
&& ci
->expr_B
))
226 av_log(NULL
, AV_LOG_ERROR
, "You must specify expressions for all or no colors.\n");
230 if (ci
->text
|| ci
->file
) {
231 ci
->fn
= imlib_load_font(font
);
233 av_log(NULL
, AV_LOG_ERROR
, "Failed to load font '%s'\n", font
);
236 imlib_context_set_font(ci
->fn
);
237 imlib_context_set_direction(IMLIB_TEXT_TO_RIGHT
);
246 av_log(NULL
, AV_LOG_ERROR
, "You must not specify both a color name and expressions for the colors.\n");
251 f
= fopen(rgbtxt
, "r");
254 f
= fopen("/usr/share/X11/rgb.txt", "r");
256 f
= fopen("/usr/lib/X11/rgb.txt", "r");
259 av_log(NULL
, AV_LOG_ERROR
, "Failed to find RGB color names file\n");
262 while (fgets(buff
, sizeof(buff
), f
)) {
266 if (sscanf(buff
, "%d %d %d %64s", &r
, &g
, &b
, colname
) == 4 &&
267 strcasecmp(colname
, color
) == 0) {
271 /* fprintf(stderr, "%s -> %d,%d,%d\n", colname, r, g, b); */
278 av_log(NULL
, AV_LOG_ERROR
, "Unable to find color '%s' in rgb.txt\n", color
);
281 } else if (ci
->eval_colors
) {
282 if (!(ci
->eval_r
= ff_parse(ci
->expr_R
, const_names
, NULL
, NULL
, NULL
, NULL
, &error
))){
283 av_log(NULL
, AV_LOG_ERROR
, "Couldn't parse R expression '%s': %s\n", ci
->expr_R
, error
);
286 if (!(ci
->eval_g
= ff_parse(ci
->expr_G
, const_names
, NULL
, NULL
, NULL
, NULL
, &error
))){
287 av_log(NULL
, AV_LOG_ERROR
, "Couldn't parse G expression '%s': %s\n", ci
->expr_G
, error
);
290 if (!(ci
->eval_b
= ff_parse(ci
->expr_B
, const_names
, NULL
, NULL
, NULL
, NULL
, &error
))){
291 av_log(NULL
, AV_LOG_ERROR
, "Couldn't parse B expression '%s': %s\n", ci
->expr_B
, error
);
297 if (!(ci
->eval_a
= ff_parse(ci
->expr_A
, const_names
, NULL
, NULL
, NULL
, NULL
, &error
))){
298 av_log(NULL
, AV_LOG_ERROR
, "Couldn't parse A expression '%s': %s\n", ci
->expr_A
, error
);
305 if (!(ci
->eval_colors
|| ci
->eval_a
))
306 imlib_context_set_color(ci
->r
, ci
->g
, ci
->b
, ci
->a
);
308 /* load the image (for example, credits for a movie) */
310 ci
->imageOverlaid
= imlib_load_image_immediately(ci
->fileImage
);
311 if (!(ci
->imageOverlaid
)){
312 av_log(NULL
, AV_LOG_ERROR
, "Couldn't load image '%s'\n", ci
->fileImage
);
315 imlib_context_set_image(ci
->imageOverlaid
);
316 ci
->imageOverlaid_width
= imlib_image_get_width();
317 ci
->imageOverlaid_height
= imlib_image_get_height();
320 if (!(ci
->eval_x
= ff_parse(ci
->expr_x
, const_names
, NULL
, NULL
, NULL
, NULL
, &error
))){
321 av_log(NULL
, AV_LOG_ERROR
, "Couldn't parse x expression '%s': %s\n", ci
->expr_x
, error
);
325 if (!(ci
->eval_y
= ff_parse(ci
->expr_y
, const_names
, NULL
, NULL
, NULL
, NULL
, &error
))){
326 av_log(NULL
, AV_LOG_ERROR
, "Couldn't parse y expression '%s': %s\n", ci
->expr_y
, error
);
333 static Imlib_Image
get_cached_image(ContextInfo
*ci
, int width
, int height
)
337 for (cache
= ci
->cache
; cache
; cache
= cache
->next
) {
338 if (width
== cache
->width
&& height
== cache
->height
)
345 static void put_cached_image(ContextInfo
*ci
, Imlib_Image image
, int width
, int height
)
347 CachedImage
*cache
= av_mallocz(sizeof(*cache
));
349 cache
->image
= image
;
350 cache
->width
= width
;
351 cache
->height
= height
;
352 cache
->next
= ci
->cache
;
356 void Process(void *ctx
, AVPicture
*picture
, enum PixelFormat pix_fmt
, int width
, int height
, int64_t pts
)
358 ContextInfo
*ci
= (ContextInfo
*) ctx
;
363 image
= get_cached_image(ci
, width
, height
);
366 image
= imlib_create_image(width
, height
);
367 put_cached_image(ci
, image
, width
, height
);
370 imlib_context_set_image(image
);
371 data
= imlib_image_get_data();
373 avpicture_fill(&picture1
, (uint8_t *) data
, PIX_FMT_RGB32
, width
, height
);
375 // if we already got a SWS context, let's realloc if is not re-useable
376 ci
->toRGB_convert_ctx
= sws_getCachedContext(ci
->toRGB_convert_ctx
,
377 width
, height
, pix_fmt
,
378 width
, height
, PIX_FMT_RGB32
,
379 sws_flags
, NULL
, NULL
, NULL
);
380 if (ci
->toRGB_convert_ctx
== NULL
) {
381 av_log(NULL
, AV_LOG_ERROR
,
382 "Cannot initialize the toRGB conversion context\n");
386 // img_convert parameters are 2 first destination, then 4 source
387 // sws_scale parameters are context, 4 first source, then 2 destination
388 sws_scale(ci
->toRGB_convert_ctx
,
389 picture
->data
, picture
->linesize
, 0, height
,
390 picture1
.data
, picture1
.linesize
);
392 imlib_image_set_has_alpha(0);
395 int wid
, hig
, h_a
, v_a
;
398 char *tbp
= ci
->text
;
399 time_t now
= time(0);
403 double const_values
[]={
406 ci
->frame_number
, // frame number (starting at zero)
407 height
, // frame height
408 width
, // frame width
409 ci
->imageOverlaid_height
, // image height
410 ci
->imageOverlaid_width
, // image width
417 int fd
= open(ci
->file
, O_RDONLY
);
420 tbp
= "[File not found]";
422 int l
= read(fd
, tbuff
, sizeof(tbuff
) - 1);
435 strftime(buff
, sizeof(buff
), tbp
, localtime(&now
));
436 else if (!(ci
->imageOverlaid
))
437 strftime(buff
, sizeof(buff
), "[No data]", localtime(&now
));
439 ci
->x
= ff_parse_eval(ci
->eval_x
, const_values
, ci
);
440 ci
->y
= ff_parse_eval(ci
->eval_y
, const_values
, ci
);
444 ci
->a
= ff_parse_eval(ci
->eval_a
, const_values
, ci
);
447 if (ci
->eval_colors
) {
448 ci
->r
= ff_parse_eval(ci
->eval_r
, const_values
, ci
);
449 ci
->g
= ff_parse_eval(ci
->eval_g
, const_values
, ci
);
450 ci
->b
= ff_parse_eval(ci
->eval_b
, const_values
, ci
);
453 if (ci
->eval_colors
|| ci
->eval_a
) {
454 imlib_context_set_color(ci
->r
, ci
->g
, ci
->b
, ci
->a
);
457 if (!(ci
->imageOverlaid
))
458 for (p
= buff
; p
; p
= q
) {
463 imlib_text_draw_with_return_metrics(ci
->x
, y
, p
, &wid
, &hig
, &h_a
, &v_a
);
467 if (ci
->imageOverlaid
) {
468 imlib_context_set_image(image
);
469 imlib_blend_image_onto_image(ci
->imageOverlaid
, 0,
470 0, 0, ci
->imageOverlaid_width
, ci
->imageOverlaid_height
,
471 ci
->x
, ci
->y
, ci
->imageOverlaid_width
, ci
->imageOverlaid_height
);
476 ci
->fromRGB_convert_ctx
= sws_getCachedContext(ci
->fromRGB_convert_ctx
,
477 width
, height
, PIX_FMT_RGB32
,
478 width
, height
, pix_fmt
,
479 sws_flags
, NULL
, NULL
, NULL
);
480 if (ci
->fromRGB_convert_ctx
== NULL
) {
481 av_log(NULL
, AV_LOG_ERROR
,
482 "Cannot initialize the fromRGB conversion context\n");
485 // img_convert parameters are 2 first destination, then 4 source
486 // sws_scale parameters are context, 4 first source, then 2 destination
487 sws_scale(ci
->fromRGB_convert_ctx
,
488 picture1
.data
, picture1
.linesize
, 0, height
,
489 picture
->data
, picture
->linesize
);