1 /*
2 * Copyright (c) 2011 Stefano Sabatini
3 * Copyright (c) 2010 S.N. Hemanth Meenakshisundaram
4 * Copyright (c) 2003 Gustavo Sverzut Barbieri <gsbarbieri@yahoo.com.br>
5 *
6 * This file is part of FFmpeg.
7 *
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 /**
24 * @file
25 * drawtext filter, based on the original vhook/drawtext.c
26 * filter by Gustavo Sverzut Barbieri
27 */
28
29 #include <sys/time.h>
31
32 #include "config.h"
49
50 #include <ft2build.h>
51 #include FT_FREETYPE_H
52 #include FT_GLYPH_H
53 #if CONFIG_FONTCONFIG
54 #include <fontconfig/fontconfig.h>
55 #endif
56
58 "dar",
59 "hsub", "vsub",
60 "line_h", "lh", ///< line height, same as max_glyph_h
61 "main_h", "h", "H", ///< height of the input video
62 "main_w", "w", "W", ///< width of the input video
63 "max_glyph_a", "ascent", ///< max glyph ascent
64 "max_glyph_d", "descent", ///< min glyph descent
65 "max_glyph_h", ///< max glyph height
66 "max_glyph_w", ///< max glyph width
67 "n", ///< number of frame
68 "sar",
69 "t", ///< timestamp expressed in seconds
70 "text_h", "th", ///< height of the rendered text
71 "text_w", "tw", ///< width of the rendered text
72 "x",
73 "y",
74 "pict_type",
75 NULL
76 };
77
79 "rand"
80 };
81
82 static double drand(
void *opaque,
double min,
double max)
83 {
85 }
86
88
91 NULL
92 };
93
113 };
114
119 };
120
124 int reinit;
///< tells if the filter is being reinited
129 FT_Vector *
positions;
///< positions for each element in the text
132 int x;
///< x position to start drawing text
133 int y;
///< y position to start drawing text
138
139 short int draw_box;
///< draw box around text - true or false
142 int fix_bounds;
///< do we let it go out of frame bounds - t/f
143
148
149 FT_Library
library;
///< freetype font library handle
150 FT_Face
face;
///< freetype font face handle
152 char *
x_expr;
///< expression for x position
153 char *
y_expr;
///< expression for y position
155 int64_t
basetime;
///< base pts time in the real world for display
157 #if FF_API_DRAWTEXT_OLD_TIMELINE
160 int draw;
///< set to zero to prevent drawing
161 #endif
166 int tc24hmax;
///< 1 if timecode is wrapped to 24 hours, 0 otherwise
167 int reload;
///< reload text file for each frame
171
172 #define OFFSET(x) offsetof(DrawTextContext, x)
173 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
174
190 #if FF_API_DRAWTEXT_OLD_TIMELINE
192 #endif
193
198
205 {
"fix_bounds",
"if true, check and fix text coords to avoid clipping",
OFFSET(fix_bounds),
AV_OPT_TYPE_INT, {.i64=1}, 0, 1,
FLAGS},
206 {
"start_number",
"start frame number for n/frame_num variable",
OFFSET(start_number),
AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX,
FLAGS},
207
208 /* FT_LOAD_* flags */
209 {
"ft_load_flags",
"set font loading flags for libfreetype",
OFFSET(ft_load_flags),
AV_OPT_TYPE_FLAGS, { .i64 = FT_LOAD_DEFAULT | FT_LOAD_RENDER}, 0, INT_MAX,
FLAGS,
"ft_load_flags" },
210 {
"default", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_DEFAULT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
211 {
"no_scale", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_SCALE }, .flags =
FLAGS, .unit =
"ft_load_flags" },
212 {
"no_hinting", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_HINTING }, .flags =
FLAGS, .unit =
"ft_load_flags" },
213 {
"render", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_RENDER }, .flags =
FLAGS, .unit =
"ft_load_flags" },
214 {
"no_bitmap", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_BITMAP }, .flags =
FLAGS, .unit =
"ft_load_flags" },
215 {
"vertical_layout", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_VERTICAL_LAYOUT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
216 {
"force_autohint", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_FORCE_AUTOHINT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
217 {
"crop_bitmap", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_CROP_BITMAP }, .flags =
FLAGS, .unit =
"ft_load_flags" },
218 {
"pedantic", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_PEDANTIC }, .flags =
FLAGS, .unit =
"ft_load_flags" },
219 {
"ignore_global_advance_width", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH }, .flags =
FLAGS, .unit =
"ft_load_flags" },
220 {
"no_recurse", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_RECURSE }, .flags =
FLAGS, .unit =
"ft_load_flags" },
221 {
"ignore_transform", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_IGNORE_TRANSFORM }, .flags =
FLAGS, .unit =
"ft_load_flags" },
222 {
"monochrome", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_MONOCHROME }, .flags =
FLAGS, .unit =
"ft_load_flags" },
223 {
"linear_design", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_LINEAR_DESIGN }, .flags =
FLAGS, .unit =
"ft_load_flags" },
224 {
"no_autohint", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_AUTOHINT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
225 { NULL }
226 };
227
229
230 #undef __FTERRORS_H__
231 #define FT_ERROR_START_LIST {
232 #define FT_ERRORDEF(e, v, s) { (e), (s) },
233 #define FT_ERROR_END_LIST { 0, NULL } };
234
236 {
240 #include FT_ERRORS_H
241
242 #define FT_ERRMSG(e) ft_errors[e].err_msg
243
244 typedef struct {
245 FT_Glyph *glyph;
246 uint32_t code;
247 FT_Bitmap bitmap; ///< array holding bitmaps of font
248 FT_BBox bbox;
249 int advance;
250 int bitmap_left;
251 int bitmap_top;
252 } Glyph;
253
255 {
256 const Glyph *
a = key, *bb =
b;
257 int64_t diff = (int64_t)a->code - (int64_t)bb->code;
258 return diff > 0 ? 1 : diff < 0 ? -1 : 0;
259 }
260
261 /**
262 * Load glyphs corresponding to the UTF-32 codepoint code.
263 */
265 {
267 Glyph *glyph;
270
271 /* load glyph into s->face->glyph */
274
275 /* save glyph */
277 !(glyph->glyph =
av_mallocz(
sizeof(*glyph->glyph)))) {
279 goto error;
280 }
281 glyph->code = code;
282
283 if (FT_Get_Glyph(s->
face->glyph, glyph->glyph)) {
285 goto error;
286 }
287
288 glyph->bitmap = s->
face->glyph->bitmap;
289 glyph->bitmap_left = s->
face->glyph->bitmap_left;
290 glyph->bitmap_top = s->
face->glyph->bitmap_top;
291 glyph->advance = s->
face->glyph->advance.x >> 6;
292
293 /* measure text height to calculate text_height (or the maximum text height) */
294 FT_Glyph_Get_CBox(*glyph->glyph, ft_glyph_bbox_pixels, &glyph->bbox);
295
296 /* cache the newly created glyph */
299 goto error;
300 }
302
303 if (glyph_ptr)
304 *glyph_ptr = glyph;
305 return 0;
306
307 error:
308 if (glyph)
313 }
314
316 const char **error)
317 {
319 int err;
320
321 err = FT_New_Face(s->
library, path, index, &s->
face);
322 if (err) {
325 }
326 return 0;
327 }
328
329 #if CONFIG_FONTCONFIG
330 static int load_font_fontconfig(
AVFilterContext *ctx,
const char **error)
331 {
333 FcConfig *fontconfig;
334 FcPattern *pattern, *fpat;
335 FcResult result = FcResultMatch;
336 FcChar8 *filename;
339
340 fontconfig = FcInitLoadConfigAndFonts();
341 if (!fontconfig) {
342 *error = "impossible to init fontconfig\n";
344 }
346 (
uint8_t *)(intptr_t)
"default");
347 if (!pattern) {
348 *error = "could not parse fontconfig pattern";
350 }
351 if (!FcConfigSubstitute(fontconfig, pattern, FcMatchPattern)) {
352 *error = "could not substitue fontconfig options"; /* very unlikely */
354 }
355 FcDefaultSubstitute(pattern);
356 fpat = FcFontMatch(fontconfig, pattern, &result);
357 if (!fpat || result != FcResultMatch) {
358 *error = "impossible to find a matching font";
360 }
361 if (FcPatternGetString (fpat, FC_FILE, 0, &filename) != FcResultMatch ||
362 FcPatternGetInteger(fpat, FC_INDEX, 0, &index ) != FcResultMatch ||
363 FcPatternGetDouble (fpat, FC_SIZE, 0, &size ) != FcResultMatch) {
364 *error = "impossible to find font information";
366 }
371 if (err)
372 return err;
373 FcPatternDestroy(fpat);
374 FcPatternDestroy(pattern);
375 FcConfigDestroy(fontconfig);
376 return 0;
377 }
378 #endif
379
381 {
383 int err;
384 const char *error = "unknown error\n";
385
386 /* load the face, and set up the encoding, which is by default UTF-8 */
388 if (!err)
389 return 0;
390 #if CONFIG_FONTCONFIG
391 err = load_font_fontconfig(ctx, &error);
392 if (!err)
393 return 0;
394 #endif
397 return err;
398 }
399
401 {
403 int err;
405 size_t textbuf_size;
406
409 "The text file '%s' could not be read or is empty\n",
411 return err;
412 }
413
416 memcpy(s->
text, textbuf, textbuf_size);
417 s->
text[textbuf_size] = 0;
419
420 return 0;
421 }
422
424 {
425 int err;
427 Glyph *glyph;
428
429 #if FF_API_DRAWTEXT_OLD_TIMELINE
432 "you are encouraged to use the generic timeline support through the 'enable' option\n");
433 #endif
434
435 if (!s->
fontfile && !CONFIG_FONTCONFIG) {
438 }
439
443 "Both text and text file provided. Please provide only one\n");
445 }
447 return err;
448 }
449
452
456 if (ret < 0)
462 }
463
466 "Either text, a valid file or a timecode must be provided\n");
468 }
469
470 if ((err = FT_Init_FreeType(&(s->
library)))) {
472 "Could not load FreeType: %s\n",
FT_ERRMSG(err));
474 }
475
477 if (err)
478 return err;
481 if ((err = FT_Set_Pixel_Sizes(s->
face, 0, s->
fontsize))) {
485 }
486
488
489 /* load the fallback glyph with code 0 */
491
492 /* set the tabsize in pixels */
493 if ((err =
load_glyph(ctx, &glyph,
' ')) < 0) {
495 return err;
496 }
498
500 (strchr(s->
text,
'%') || strchr(s->
text,
'\\')))
502
504
505 return 0;
506 }
507
509 {
511 return 0;
512 }
513
515 {
517
518 FT_Done_Glyph(*glyph->glyph);
521 return 0;
522 }
523
525 {
527
530 #if FF_API_DRAWTEXT_OLD_TIMELINE
533 #endif
536
537
541
542 FT_Done_Face(s->
face);
544
546 }
547
549 {
550 return c == '\n' || c == '\r' || c == '\f' || c == '\v';
551 }
552
554 {
558
563
573
575
578 #if FF_API_DRAWTEXT_OLD_TIMELINE
581 #else
583 #endif
584
589
591 #if FF_API_DRAWTEXT_OLD_TIMELINE
596 #endif
597
598 return 0;
599 }
600
602 {
604
605 if (!strcmp(cmd, "reinit")) {
609 if ((ret =
init(ctx)) < 0)
612 }
613
615 }
616
618 char *fct,
unsigned argc,
char **argv,
int tag)
619 {
621
623 return 0;
624 }
625
627 char *fct,
unsigned argc,
char **argv,
int tag)
628 {
630
632 return 0;
633 }
634
636 char *fct,
unsigned argc,
char **argv,
int tag)
637 {
639
641 return 0;
642 }
643
645 char *fct,
unsigned argc,
char **argv,
int tag)
646 {
649
652 return 0;
653 }
654
655 #if !HAVE_LOCALTIME_R
657 {
658 *tm = *localtime(t);
659 }
660 #endif
661
663 char *fct,
unsigned argc,
char **argv,
int tag)
664 {
665 const char *
fmt = argc ? argv[0] :
"%Y-%m-%d %H:%M:%S";
666 time_t now;
667 struct tm tm;
668
669 time(&now);
670 if (tag == 'L')
672 else
673 tm = *gmtime(&now);
675 return 0;
676 }
677
679 char *fct,
unsigned argc,
char **argv,
int tag)
680 {
684
688 if (ret < 0)
690 "Expression '%s' for the expr text expansion function is not valid\n",
691 argv[0]);
692 else
694
696 }
697
701 int tag;
/**< opaque argument to func */
713 };
714
716 unsigned argc, char **argv)
717 {
718 unsigned i;
719
722 continue;
727 }
732 }
733 break;
734 }
738 }
740 }
741
743 {
744 const char *text = *rtext;
745 char *argv[16] = { NULL };
746 unsigned argc = 0, i;
748
749 if (*text != '{') {
752 }
753 text++;
754 while (1) {
758 }
759 if (!*text) {
763 }
765 av_freep(&argv[--argc]);
/* error will be caught later */
766 if (*text == '}')
767 break;
768 text++;
769 }
770
771 if ((ret =
eval_function(ctx, bp, argv[0], argc - 1, argv + 1)) < 0)
773 ret = 0;
774 *rtext = (char *)text + 1;
775
777 for (i = 0; i < argc; i++)
780 }
781
783 {
785 char *text = s->
text;
788
790 while (*text) {
791 if (*text == '\\' && text[1]) {
793 text += 2;
794 } else if (*text == '%') {
795 text++;
798 } else {
800 text++;
801 }
802 }
805 return 0;
806 }
807
810 {
812 uint32_t code = 0;
813 int i, x1, y1;
815 Glyph *glyph = NULL;
816
817 for (i = 0, p = text; *p; i++) {
820
821 /* skip new line chars, just go to new line */
822 if (code == '\n' || code == '\r' || code == '\t')
823 continue;
824
825 dummy.code = code;
827
828 if (glyph->bitmap.pixel_mode != FT_PIXEL_MODE_MONO &&
829 glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
831
834
837 glyph->bitmap.buffer, glyph->bitmap.pitch,
838 glyph->bitmap.width, glyph->bitmap.rows,
839 glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO ? 0 : 3,
840 0, x1, y1);
841 }
842
843 return 0;
844 }
845
848 {
851
852 uint32_t code = 0, prev_code = 0;
853 int x = 0,
y = 0, i = 0,
ret;
854 int max_text_line_w = 0,
len;
855 int box_w, box_h;
856 char *text;
858 int y_min = 32000, y_max = -32000;
859 int x_min = 32000, x_max = -32000;
861 Glyph *glyph = NULL, *prev_glyph = NULL;
863
864 time_t now = time(0);
865 struct tm ltime;
867
869
872
876 break;
880 break;
884 break;
885 }
886
892 }
893
902 }
903
904 x = 0;
906
907 /* load and cache glyphs */
908 for (i = 0, p = text; *p; i++) {
910
911 /* get glyph */
912 dummy.code = code;
914 if (!glyph) {
916 }
917
918 y_min =
FFMIN(glyph->bbox.yMin, y_min);
919 y_max =
FFMAX(glyph->bbox.yMax, y_max);
920 x_min =
FFMIN(glyph->bbox.xMin, x_min);
921 x_max =
FFMAX(glyph->bbox.xMax, x_max);
922 }
925
926 /* compute and save position for each glyph */
927 glyph = NULL;
928 for (i = 0, p = text; *p; i++) {
930
931 /* skip the \n in the sequence \r\n */
932 if (prev_code == '\r' && code == '\n')
933 continue;
934
935 prev_code = code;
937
938 max_text_line_w =
FFMAX(max_text_line_w, x);
940 x = 0;
941 continue;
942 }
943
944 /* get glyph */
945 prev_glyph = glyph;
946 dummy.code = code;
948
949 /* kerning */
951 FT_Get_Kerning(s->
face, prev_glyph->code, glyph->code,
952 ft_kerning_default, &delta);
953 x += delta.x >> 6;
954 }
955
956 /* save position */
957 s->
positions[i].x = x + glyph->bitmap_left;
958 s->
positions[i].y =
y - glyph->bitmap_top + y_max;
960 else x += glyph->advance;
961 }
962
963 max_text_line_w =
FFMAX(x, max_text_line_w);
964
967
972
974
978 #if FF_API_DRAWTEXT_OLD_TIMELINE
981
983 return 0;
984 }
986 return 0;
987 #endif
988
989 box_w =
FFMIN(width - 1 , max_text_line_w);
991
992 /* draw box */
996 s->
x, s->
y, box_w, box_h);
997
1002 }
1003
1007
1008 return 0;
1009 }
1010
1012 {
1017
1021
1025
1028
1030
1035
1037 }
1038
1040 {
1045 .needs_writable = 1,
1046 },
1047 { NULL }
1048 };
1049
1051 {
1054 },
1055 { NULL }
1056 };
1057
1060 .description =
NULL_IF_CONFIG_SMALL(
"Draw text on top of video frames using libfreetype library."),
1062 .priv_class = &drawtext_class,
1066 .
inputs = avfilter_vf_drawtext_inputs,
1067 .
outputs = avfilter_vf_drawtext_outputs,
1071 #else
1073 #endif
1074 };