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 <freetype/config/ftheader.h>
52 #include FT_FREETYPE_H
53 #include FT_GLYPH_H
54 #if CONFIG_FONTCONFIG
55 #include <fontconfig/fontconfig.h>
56 #endif
57
59 "dar",
60 "hsub", "vsub",
61 "line_h", "lh", ///< line height, same as max_glyph_h
62 "main_h", "h", "H", ///< height of the input video
63 "main_w", "w", "W", ///< width of the input video
64 "max_glyph_a", "ascent", ///< max glyph ascent
65 "max_glyph_d", "descent", ///< min glyph descent
66 "max_glyph_h", ///< max glyph height
67 "max_glyph_w", ///< max glyph width
68 "n", ///< number of frame
69 "sar",
70 "t", ///< timestamp expressed in seconds
71 "text_h", "th", ///< height of the rendered text
72 "text_w", "tw", ///< width of the rendered text
73 "x",
74 "y",
75 "pict_type",
76 NULL
77 };
78
80 "rand"
81 };
82
83 static double drand(
void *opaque,
double min,
double max)
84 {
86 }
87
89
92 NULL
93 };
94
114 };
115
120 };
121
125 int reinit;
///< tells if the filter is being reinited
130 FT_Vector *
positions;
///< positions for each element in the text
133 int x;
///< x position to start drawing text
134 int y;
///< y position to start drawing text
139
140 short int draw_box;
///< draw box around text - true or false
143 int fix_bounds;
///< do we let it go out of frame bounds - t/f
144
149
150 FT_Library
library;
///< freetype font library handle
151 FT_Face
face;
///< freetype font face handle
153 char *
x_expr;
///< expression for x position
154 char *
y_expr;
///< expression for y position
156 int64_t
basetime;
///< base pts time in the real world for display
160 int draw;
///< set to zero to prevent drawing
165 int tc24hmax;
///< 1 if timecode is wrapped to 24 hours, 0 otherwise
166 int reload;
///< reload text file for each frame
170
171 #define OFFSET(x) offsetof(DrawTextContext, x)
172 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
173
190
195
202 {
"fix_bounds",
"if true, check and fix text coords to avoid clipping",
OFFSET(fix_bounds),
AV_OPT_TYPE_INT, {.i64=1}, 0, 1,
FLAGS},
203 {
"start_number",
"start frame number for n/frame_num variable",
OFFSET(start_number),
AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX,
FLAGS},
204
205 /* FT_LOAD_* flags */
206 {
"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" },
207 {
"default", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_DEFAULT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
208 {
"no_scale", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_SCALE }, .flags =
FLAGS, .unit =
"ft_load_flags" },
209 {
"no_hinting", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_HINTING }, .flags =
FLAGS, .unit =
"ft_load_flags" },
210 {
"render", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_RENDER }, .flags =
FLAGS, .unit =
"ft_load_flags" },
211 {
"no_bitmap", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_BITMAP }, .flags =
FLAGS, .unit =
"ft_load_flags" },
212 {
"vertical_layout", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_VERTICAL_LAYOUT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
213 {
"force_autohint", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_FORCE_AUTOHINT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
214 {
"crop_bitmap", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_CROP_BITMAP }, .flags =
FLAGS, .unit =
"ft_load_flags" },
215 {
"pedantic", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_PEDANTIC }, .flags =
FLAGS, .unit =
"ft_load_flags" },
216 {
"ignore_global_advance_width", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH }, .flags =
FLAGS, .unit =
"ft_load_flags" },
217 {
"no_recurse", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_RECURSE }, .flags =
FLAGS, .unit =
"ft_load_flags" },
218 {
"ignore_transform", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_IGNORE_TRANSFORM }, .flags =
FLAGS, .unit =
"ft_load_flags" },
219 {
"monochrome", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_MONOCHROME }, .flags =
FLAGS, .unit =
"ft_load_flags" },
220 {
"linear_design", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_LINEAR_DESIGN }, .flags =
FLAGS, .unit =
"ft_load_flags" },
221 {
"no_autohint", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_AUTOHINT }, .flags =
FLAGS, .unit =
"ft_load_flags" },
222 { NULL},
223 };
224
226
227 #undef __FTERRORS_H__
228 #define FT_ERROR_START_LIST {
229 #define FT_ERRORDEF(e, v, s) { (e), (s) },
230 #define FT_ERROR_END_LIST { 0, NULL } };
231
233 {
237 #include FT_ERRORS_H
238
239 #define FT_ERRMSG(e) ft_errors[e].err_msg
240
241 typedef struct {
242 FT_Glyph *glyph;
243 uint32_t code;
244 FT_Bitmap bitmap; ///< array holding bitmaps of font
245 FT_BBox bbox;
246 int advance;
247 int bitmap_left;
248 int bitmap_top;
249 } Glyph;
250
252 {
253 const Glyph *
a = key, *bb =
b;
254 int64_t
diff = (int64_t)a->code - (int64_t)bb->code;
255 return diff > 0 ? 1 : diff < 0 ? -1 : 0;
256 }
257
258 /**
259 * Load glyphs corresponding to the UTF-32 codepoint code.
260 */
262 {
264 Glyph *glyph;
267
268 /* load glyph into s->face->glyph */
271
272 /* save glyph */
274 !(glyph->glyph =
av_mallocz(
sizeof(*glyph->glyph)))) {
276 goto error;
277 }
278 glyph->code = code;
279
280 if (FT_Get_Glyph(s->
face->glyph, glyph->glyph)) {
282 goto error;
283 }
284
285 glyph->bitmap = s->
face->glyph->bitmap;
286 glyph->bitmap_left = s->
face->glyph->bitmap_left;
287 glyph->bitmap_top = s->
face->glyph->bitmap_top;
288 glyph->advance = s->
face->glyph->advance.x >> 6;
289
290 /* measure text height to calculate text_height (or the maximum text height) */
291 FT_Glyph_Get_CBox(*glyph->glyph, ft_glyph_bbox_pixels, &glyph->bbox);
292
293 /* cache the newly created glyph */
296 goto error;
297 }
299
300 if (glyph_ptr)
301 *glyph_ptr = glyph;
302 return 0;
303
304 error:
305 if (glyph)
310 }
311
313 const char **error)
314 {
316 int err;
317
318 err = FT_New_Face(s->
library, path, index, &s->
face);
319 if (err) {
322 }
323 return 0;
324 }
325
326 #if CONFIG_FONTCONFIG
327 static int load_font_fontconfig(
AVFilterContext *ctx,
const char **error)
328 {
330 FcConfig *fontconfig;
331 FcPattern *pattern, *fpat;
332 FcResult result = FcResultMatch;
333 FcChar8 *filename;
336
337 fontconfig = FcInitLoadConfigAndFonts();
338 if (!fontconfig) {
339 *error = "impossible to init fontconfig\n";
341 }
343 (
uint8_t *)(intptr_t)
"default");
344 if (!pattern) {
345 *error = "could not parse fontconfig pattern";
347 }
348 if (!FcConfigSubstitute(fontconfig, pattern, FcMatchPattern)) {
349 *error = "could not substitue fontconfig options"; /* very unlikely */
351 }
352 FcDefaultSubstitute(pattern);
353 fpat = FcFontMatch(fontconfig, pattern, &result);
354 if (!fpat || result != FcResultMatch) {
355 *error = "impossible to find a matching font";
357 }
358 if (FcPatternGetString (fpat, FC_FILE, 0, &filename) != FcResultMatch ||
359 FcPatternGetInteger(fpat, FC_INDEX, 0, &index ) != FcResultMatch ||
360 FcPatternGetDouble (fpat, FC_SIZE, 0, &size ) != FcResultMatch) {
361 *error = "impossible to find font information";
363 }
368 if (err)
369 return err;
370 FcPatternDestroy(fpat);
371 FcPatternDestroy(pattern);
372 FcConfigDestroy(fontconfig);
373 return 0;
374 }
375 #endif
376
378 {
380 int err;
381 const char *error = "unknown error\n";
382
383 /* load the face, and set up the encoding, which is by default UTF-8 */
385 if (!err)
386 return 0;
387 #if CONFIG_FONTCONFIG
388 err = load_font_fontconfig(ctx, &error);
389 if (!err)
390 return 0;
391 #endif
394 return err;
395 }
396
398 {
400 int err;
402 size_t textbuf_size;
403
406 "The text file '%s' could not be read or is empty\n",
408 return err;
409 }
410
413 memcpy(s->
text, textbuf, textbuf_size);
414 s->
text[textbuf_size] = 0;
416
417 return 0;
418 }
419
421 {
422 int err;
424 Glyph *glyph;
425
426 if (!s->
fontfile && !CONFIG_FONTCONFIG) {
429 }
430
434 "Both text and text file provided. Please provide only one\n");
436 }
438 return err;
439 }
440
443
447 if (ret < 0)
453 }
454
457 "Either text, a valid file or a timecode must be provided\n");
459 }
460
461 if ((err = FT_Init_FreeType(&(s->
library)))) {
463 "Could not load FreeType: %s\n",
FT_ERRMSG(err));
465 }
466
468 if (err)
469 return err;
472 if ((err = FT_Set_Pixel_Sizes(s->
face, 0, s->
fontsize))) {
476 }
477
479
480 /* load the fallback glyph with code 0 */
482
483 /* set the tabsize in pixels */
484 if ((err =
load_glyph(ctx, &glyph,
' ')) < 0) {
486 return err;
487 }
489
491 (strchr(s->
text,
'%') || strchr(s->
text,
'\\')))
493
495
496 return 0;
497 }
498
500 {
502 return 0;
503 }
504
506 {
508
509 FT_Done_Glyph(*glyph->glyph);
512 return 0;
513 }
514
516 {
518
525
526
530
531 FT_Done_Face(s->
face);
533
535 }
536
538 {
539 return c == '\n' || c == '\r' || c == '\f' || c == '\v';
540 }
541
543 {
547
552
562
564
575
577
578 return 0;
579 }
580
582 {
584
585 if (!strcmp(cmd, "reinit")) {
589 if ((ret =
init(ctx)) < 0)
592 }
593
595 }
596
598 char *fct,
unsigned argc,
char **argv,
int tag)
599 {
601
603 return 0;
604 }
605
607 char *fct,
unsigned argc,
char **argv,
int tag)
608 {
610
612 return 0;
613 }
614
616 char *fct,
unsigned argc,
char **argv,
int tag)
617 {
619
621 return 0;
622 }
623
625 char *fct,
unsigned argc,
char **argv,
int tag)
626 {
629
632 return 0;
633 }
634
635 #if !HAVE_LOCALTIME_R
637 {
638 *tm = *localtime(t);
639 }
640 #endif
641
643 char *fct,
unsigned argc,
char **argv,
int tag)
644 {
645 const char *
fmt = argc ? argv[0] :
"%Y-%m-%d %H:%M:%S";
646 time_t now;
647 struct tm tm;
648
649 time(&now);
650 if (tag == 'L')
652 else
653 tm = *gmtime(&now);
655 return 0;
656 }
657
659 char *fct,
unsigned argc,
char **argv,
int tag)
660 {
664
668 if (ret < 0)
670 "Expression '%s' for the expr text expansion function is not valid\n",
671 argv[0]);
672 else
674
676 }
677
681 int tag;
/**< opaque argument to func */
693 };
694
696 unsigned argc, char **argv)
697 {
698 unsigned i;
699
702 continue;
707 }
712 }
713 break;
714 }
718 }
720 }
721
723 {
724 const char *text = *rtext;
725 char *argv[16] = { NULL };
726 unsigned argc = 0, i;
728
729 if (*text != '{') {
732 }
733 text++;
734 while (1) {
738 }
739 if (!*text) {
743 }
745 av_freep(&argv[--argc]);
/* error will be caught later */
746 if (*text == '}')
747 break;
748 text++;
749 }
750
751 if ((ret =
eval_function(ctx, bp, argv[0], argc - 1, argv + 1)) < 0)
753 ret = 0;
754 *rtext = (char *)text + 1;
755
757 for (i = 0; i < argc; i++)
760 }
761
763 {
765 char *text = s->
text;
768
770 while (*text) {
771 if (*text == '\\' && text[1]) {
773 text += 2;
774 } else if (*text == '%') {
775 text++;
778 } else {
780 text++;
781 }
782 }
785 return 0;
786 }
787
790 {
792 uint32_t code = 0;
793 int i, x1, y1;
795 Glyph *glyph = NULL;
796
797 for (i = 0, p = text; *p; i++) {
800
801 /* skip new line chars, just go to new line */
802 if (code == '\n' || code == '\r' || code == '\t')
803 continue;
804
805 dummy.code = code;
807
808 if (glyph->bitmap.pixel_mode != FT_PIXEL_MODE_MONO &&
809 glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
811
814
817 glyph->bitmap.buffer, glyph->bitmap.pitch,
818 glyph->bitmap.width, glyph->bitmap.rows,
819 glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO ? 0 : 3,
820 0, x1, y1);
821 }
822
823 return 0;
824 }
825
828 {
831
832 uint32_t code = 0, prev_code = 0;
833 int x = 0,
y = 0, i = 0,
ret;
834 int max_text_line_w = 0,
len;
835 int box_w, box_h;
836 char *text = s->
text;
838 int y_min = 32000, y_max = -32000;
839 int x_min = 32000, x_max = -32000;
841 Glyph *glyph = NULL, *prev_glyph = NULL;
843
844 time_t now = time(0);
845 struct tm ltime;
847
849
852
856 break;
860 break;
864 break;
865 }
866
872 }
873
882 }
883
884 x = 0;
886
887 /* load and cache glyphs */
888 for (i = 0, p = text; *p; i++) {
890
891 /* get glyph */
892 dummy.code = code;
894 if (!glyph) {
896 }
897
898 y_min =
FFMIN(glyph->bbox.yMin, y_min);
899 y_max =
FFMAX(glyph->bbox.yMax, y_max);
900 x_min =
FFMIN(glyph->bbox.xMin, x_min);
901 x_max =
FFMAX(glyph->bbox.xMax, x_max);
902 }
905
906 /* compute and save position for each glyph */
907 glyph = NULL;
908 for (i = 0, p = text; *p; i++) {
910
911 /* skip the \n in the sequence \r\n */
912 if (prev_code == '\r' && code == '\n')
913 continue;
914
915 prev_code = code;
917
918 max_text_line_w =
FFMAX(max_text_line_w, x);
920 x = 0;
921 continue;
922 }
923
924 /* get glyph */
925 prev_glyph = glyph;
926 dummy.code = code;
928
929 /* kerning */
931 FT_Get_Kerning(s->
face, prev_glyph->code, glyph->code,
932 ft_kerning_default, &delta);
933 x += delta.x >> 6;
934 }
935
936 /* save position */
937 s->
positions[i].x = x + glyph->bitmap_left;
938 s->
positions[i].y =
y - glyph->bitmap_top + y_max;
940 else x += glyph->advance;
941 }
942
943 max_text_line_w =
FFMAX(x, max_text_line_w);
944
947
952
954
959
961 return 0;
962
963 box_w =
FFMIN(width - 1 , max_text_line_w);
965
966 /* draw box */
970 s->
x, s->
y, box_w, box_h);
971
976 }
977
981
982 return 0;
983 }
984
986 {
991
995
999
1002
1004
1009
1011 }
1012
1014 {
1020 .needs_writable = 1,
1021 },
1022 { NULL }
1023 };
1024
1026 {
1029 },
1030 { NULL }
1031 };
1032
1035 .description =
NULL_IF_CONFIG_SMALL(
"Draw text on top of video frames using libfreetype library."),
1037 .priv_class = &drawtext_class,
1041
1042 .
inputs = avfilter_vf_drawtext_inputs,
1043 .
outputs = avfilter_vf_drawtext_outputs,
1045 };