yading@10
|
1 /*
|
yading@10
|
2 * Copyright (c) 2011 Stefano Sabatini
|
yading@10
|
3 * Copyright (c) 2010 S.N. Hemanth Meenakshisundaram
|
yading@10
|
4 * Copyright (c) 2003 Gustavo Sverzut Barbieri <gsbarbieri@yahoo.com.br>
|
yading@10
|
5 *
|
yading@10
|
6 * This file is part of FFmpeg.
|
yading@10
|
7 *
|
yading@10
|
8 * FFmpeg is free software; you can redistribute it and/or
|
yading@10
|
9 * modify it under the terms of the GNU Lesser General Public
|
yading@10
|
10 * License as published by the Free Software Foundation; either
|
yading@10
|
11 * version 2.1 of the License, or (at your option) any later version.
|
yading@10
|
12 *
|
yading@10
|
13 * FFmpeg is distributed in the hope that it will be useful,
|
yading@10
|
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
yading@10
|
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
yading@10
|
16 * Lesser General Public License for more details.
|
yading@10
|
17 *
|
yading@10
|
18 * You should have received a copy of the GNU Lesser General Public
|
yading@10
|
19 * License along with FFmpeg; if not, write to the Free Software
|
yading@10
|
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
yading@10
|
21 */
|
yading@10
|
22
|
yading@10
|
23 /**
|
yading@10
|
24 * @file
|
yading@10
|
25 * drawtext filter, based on the original vhook/drawtext.c
|
yading@10
|
26 * filter by Gustavo Sverzut Barbieri
|
yading@10
|
27 */
|
yading@10
|
28
|
yading@10
|
29 #include <sys/time.h>
|
yading@10
|
30 #include <time.h>
|
yading@10
|
31
|
yading@10
|
32 #include "config.h"
|
yading@10
|
33 #include "libavutil/avstring.h"
|
yading@10
|
34 #include "libavutil/bprint.h"
|
yading@10
|
35 #include "libavutil/common.h"
|
yading@10
|
36 #include "libavutil/file.h"
|
yading@10
|
37 #include "libavutil/eval.h"
|
yading@10
|
38 #include "libavutil/opt.h"
|
yading@10
|
39 #include "libavutil/random_seed.h"
|
yading@10
|
40 #include "libavutil/parseutils.h"
|
yading@10
|
41 #include "libavutil/timecode.h"
|
yading@10
|
42 #include "libavutil/tree.h"
|
yading@10
|
43 #include "libavutil/lfg.h"
|
yading@10
|
44 #include "avfilter.h"
|
yading@10
|
45 #include "drawutils.h"
|
yading@10
|
46 #include "formats.h"
|
yading@10
|
47 #include "internal.h"
|
yading@10
|
48 #include "video.h"
|
yading@10
|
49
|
yading@10
|
50 #include <ft2build.h>
|
yading@10
|
51 #include <freetype/config/ftheader.h>
|
yading@10
|
52 #include FT_FREETYPE_H
|
yading@10
|
53 #include FT_GLYPH_H
|
yading@10
|
54 #if CONFIG_FONTCONFIG
|
yading@10
|
55 #include <fontconfig/fontconfig.h>
|
yading@10
|
56 #endif
|
yading@10
|
57
|
yading@10
|
58 static const char *const var_names[] = {
|
yading@10
|
59 "dar",
|
yading@10
|
60 "hsub", "vsub",
|
yading@10
|
61 "line_h", "lh", ///< line height, same as max_glyph_h
|
yading@10
|
62 "main_h", "h", "H", ///< height of the input video
|
yading@10
|
63 "main_w", "w", "W", ///< width of the input video
|
yading@10
|
64 "max_glyph_a", "ascent", ///< max glyph ascent
|
yading@10
|
65 "max_glyph_d", "descent", ///< min glyph descent
|
yading@10
|
66 "max_glyph_h", ///< max glyph height
|
yading@10
|
67 "max_glyph_w", ///< max glyph width
|
yading@10
|
68 "n", ///< number of frame
|
yading@10
|
69 "sar",
|
yading@10
|
70 "t", ///< timestamp expressed in seconds
|
yading@10
|
71 "text_h", "th", ///< height of the rendered text
|
yading@10
|
72 "text_w", "tw", ///< width of the rendered text
|
yading@10
|
73 "x",
|
yading@10
|
74 "y",
|
yading@10
|
75 NULL
|
yading@10
|
76 };
|
yading@10
|
77
|
yading@10
|
78 static const char *const fun2_names[] = {
|
yading@10
|
79 "rand"
|
yading@10
|
80 };
|
yading@10
|
81
|
yading@10
|
82 static double drand(void *opaque, double min, double max)
|
yading@10
|
83 {
|
yading@10
|
84 return min + (max-min) / UINT_MAX * av_lfg_get(opaque);
|
yading@10
|
85 }
|
yading@10
|
86
|
yading@10
|
87 typedef double (*eval_func2)(void *, double a, double b);
|
yading@10
|
88
|
yading@10
|
89 static const eval_func2 fun2[] = {
|
yading@10
|
90 drand,
|
yading@10
|
91 NULL
|
yading@10
|
92 };
|
yading@10
|
93
|
yading@10
|
94 enum var_name {
|
yading@10
|
95 VAR_DAR,
|
yading@10
|
96 VAR_HSUB, VAR_VSUB,
|
yading@10
|
97 VAR_LINE_H, VAR_LH,
|
yading@10
|
98 VAR_MAIN_H, VAR_h, VAR_H,
|
yading@10
|
99 VAR_MAIN_W, VAR_w, VAR_W,
|
yading@10
|
100 VAR_MAX_GLYPH_A, VAR_ASCENT,
|
yading@10
|
101 VAR_MAX_GLYPH_D, VAR_DESCENT,
|
yading@10
|
102 VAR_MAX_GLYPH_H,
|
yading@10
|
103 VAR_MAX_GLYPH_W,
|
yading@10
|
104 VAR_N,
|
yading@10
|
105 VAR_SAR,
|
yading@10
|
106 VAR_T,
|
yading@10
|
107 VAR_TEXT_H, VAR_TH,
|
yading@10
|
108 VAR_TEXT_W, VAR_TW,
|
yading@10
|
109 VAR_X,
|
yading@10
|
110 VAR_Y,
|
yading@10
|
111 VAR_VARS_NB
|
yading@10
|
112 };
|
yading@10
|
113
|
yading@10
|
114 enum expansion_mode {
|
yading@10
|
115 EXP_NONE,
|
yading@10
|
116 EXP_NORMAL,
|
yading@10
|
117 EXP_STRFTIME,
|
yading@10
|
118 };
|
yading@10
|
119
|
yading@10
|
120 typedef struct {
|
yading@10
|
121 const AVClass *class;
|
yading@10
|
122 enum expansion_mode exp_mode; ///< expansion mode to use for the text
|
yading@10
|
123 int reinit; ///< tells if the filter is being reinited
|
yading@10
|
124 uint8_t *fontfile; ///< font to be used
|
yading@10
|
125 uint8_t *text; ///< text to be drawn
|
yading@10
|
126 AVBPrint expanded_text; ///< used to contain the expanded text
|
yading@10
|
127 int ft_load_flags; ///< flags used for loading fonts, see FT_LOAD_*
|
yading@10
|
128 FT_Vector *positions; ///< positions for each element in the text
|
yading@10
|
129 size_t nb_positions; ///< number of elements of positions array
|
yading@10
|
130 char *textfile; ///< file with text to be drawn
|
yading@10
|
131 int x; ///< x position to start drawing text
|
yading@10
|
132 int y; ///< y position to start drawing text
|
yading@10
|
133 int max_glyph_w; ///< max glyph width
|
yading@10
|
134 int max_glyph_h; ///< max glyph height
|
yading@10
|
135 int shadowx, shadowy;
|
yading@10
|
136 unsigned int fontsize; ///< font size to use
|
yading@10
|
137 char *fontcolor_string; ///< font color as string
|
yading@10
|
138 char *boxcolor_string; ///< box color as string
|
yading@10
|
139 char *shadowcolor_string; ///< shadow color as string
|
yading@10
|
140
|
yading@10
|
141 short int draw_box; ///< draw box around text - true or false
|
yading@10
|
142 int use_kerning; ///< font kerning is used - true/false
|
yading@10
|
143 int tabsize; ///< tab size
|
yading@10
|
144 int fix_bounds; ///< do we let it go out of frame bounds - t/f
|
yading@10
|
145
|
yading@10
|
146 FFDrawContext dc;
|
yading@10
|
147 FFDrawColor fontcolor; ///< foreground color
|
yading@10
|
148 FFDrawColor shadowcolor; ///< shadow color
|
yading@10
|
149 FFDrawColor boxcolor; ///< background color
|
yading@10
|
150
|
yading@10
|
151 FT_Library library; ///< freetype font library handle
|
yading@10
|
152 FT_Face face; ///< freetype font face handle
|
yading@10
|
153 struct AVTreeNode *glyphs; ///< rendered glyphs, stored using the UTF-32 char code
|
yading@10
|
154 char *x_expr; ///< expression for x position
|
yading@10
|
155 char *y_expr; ///< expression for y position
|
yading@10
|
156 AVExpr *x_pexpr, *y_pexpr; ///< parsed expressions for x and y
|
yading@10
|
157 int64_t basetime; ///< base pts time in the real world for display
|
yading@10
|
158 double var_values[VAR_VARS_NB];
|
yading@10
|
159 char *draw_expr; ///< expression for draw
|
yading@10
|
160 AVExpr *draw_pexpr; ///< parsed expression for draw
|
yading@10
|
161 int draw; ///< set to zero to prevent drawing
|
yading@10
|
162 AVLFG prng; ///< random
|
yading@10
|
163 char *tc_opt_string; ///< specified timecode option string
|
yading@10
|
164 AVRational tc_rate; ///< frame rate for timecode
|
yading@10
|
165 AVTimecode tc; ///< timecode context
|
yading@10
|
166 int tc24hmax; ///< 1 if timecode is wrapped to 24 hours, 0 otherwise
|
yading@10
|
167 int frame_id;
|
yading@10
|
168 int reload; ///< reload text file for each frame
|
yading@10
|
169 } DrawTextContext;
|
yading@10
|
170
|
yading@10
|
171 #define OFFSET(x) offsetof(DrawTextContext, x)
|
yading@10
|
172 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
|
yading@10
|
173
|
yading@10
|
174 static const AVOption drawtext_options[]= {
|
yading@10
|
175 {"fontfile", "set font file", OFFSET(fontfile), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS},
|
yading@10
|
176 {"text", "set text", OFFSET(text), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS},
|
yading@10
|
177 {"textfile", "set text file", OFFSET(textfile), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS},
|
yading@10
|
178 {"fontcolor", "set foreground color", OFFSET(fontcolor_string), AV_OPT_TYPE_STRING, {.str="black"}, CHAR_MIN, CHAR_MAX, FLAGS},
|
yading@10
|
179 {"boxcolor", "set box color", OFFSET(boxcolor_string), AV_OPT_TYPE_STRING, {.str="white"}, CHAR_MIN, CHAR_MAX, FLAGS},
|
yading@10
|
180 {"shadowcolor", "set shadow color", OFFSET(shadowcolor_string), AV_OPT_TYPE_STRING, {.str="black"}, CHAR_MIN, CHAR_MAX, FLAGS},
|
yading@10
|
181 {"box", "set box", OFFSET(draw_box), AV_OPT_TYPE_INT, {.i64=0}, 0, 1 , FLAGS},
|
yading@10
|
182 {"fontsize", "set font size", OFFSET(fontsize), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX , FLAGS},
|
yading@10
|
183 {"x", "set x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str="0"}, CHAR_MIN, CHAR_MAX, FLAGS},
|
yading@10
|
184 {"y", "set y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str="0"}, CHAR_MIN, CHAR_MAX, FLAGS},
|
yading@10
|
185 {"shadowx", "set x", OFFSET(shadowx), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX , FLAGS},
|
yading@10
|
186 {"shadowy", "set y", OFFSET(shadowy), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX , FLAGS},
|
yading@10
|
187 {"tabsize", "set tab size", OFFSET(tabsize), AV_OPT_TYPE_INT, {.i64=4}, 0, INT_MAX , FLAGS},
|
yading@10
|
188 {"basetime", "set base time", OFFSET(basetime), AV_OPT_TYPE_INT64, {.i64=AV_NOPTS_VALUE}, INT64_MIN, INT64_MAX , FLAGS},
|
yading@10
|
189 {"draw", "if false do not draw", OFFSET(draw_expr), AV_OPT_TYPE_STRING, {.str="1"}, CHAR_MIN, CHAR_MAX, FLAGS},
|
yading@10
|
190
|
yading@10
|
191 {"expansion", "set the expansion mode", OFFSET(exp_mode), AV_OPT_TYPE_INT, {.i64=EXP_NORMAL}, 0, 2, FLAGS, "expansion"},
|
yading@10
|
192 {"none", "set no expansion", OFFSET(exp_mode), AV_OPT_TYPE_CONST, {.i64=EXP_NONE}, 0, 0, FLAGS, "expansion"},
|
yading@10
|
193 {"normal", "set normal expansion", OFFSET(exp_mode), AV_OPT_TYPE_CONST, {.i64=EXP_NORMAL}, 0, 0, FLAGS, "expansion"},
|
yading@10
|
194 {"strftime", "set strftime expansion (deprecated)", OFFSET(exp_mode), AV_OPT_TYPE_CONST, {.i64=EXP_STRFTIME}, 0, 0, FLAGS, "expansion"},
|
yading@10
|
195
|
yading@10
|
196 {"timecode", "set initial timecode", OFFSET(tc_opt_string), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS},
|
yading@10
|
197 {"tc24hmax", "set 24 hours max (timecode only)", OFFSET(tc24hmax), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS},
|
yading@10
|
198 {"timecode_rate", "set rate (timecode only)", OFFSET(tc_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, FLAGS},
|
yading@10
|
199 {"r", "set rate (timecode only)", OFFSET(tc_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, FLAGS},
|
yading@10
|
200 {"rate", "set rate (timecode only)", OFFSET(tc_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, FLAGS},
|
yading@10
|
201 {"reload", "reload text file for each frame", OFFSET(reload), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS},
|
yading@10
|
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},
|
yading@10
|
203
|
yading@10
|
204 /* FT_LOAD_* flags */
|
yading@10
|
205 { "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" },
|
yading@10
|
206 { "default", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_DEFAULT }, .flags = FLAGS, .unit = "ft_load_flags" },
|
yading@10
|
207 { "no_scale", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_SCALE }, .flags = FLAGS, .unit = "ft_load_flags" },
|
yading@10
|
208 { "no_hinting", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_HINTING }, .flags = FLAGS, .unit = "ft_load_flags" },
|
yading@10
|
209 { "render", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_RENDER }, .flags = FLAGS, .unit = "ft_load_flags" },
|
yading@10
|
210 { "no_bitmap", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_BITMAP }, .flags = FLAGS, .unit = "ft_load_flags" },
|
yading@10
|
211 { "vertical_layout", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_VERTICAL_LAYOUT }, .flags = FLAGS, .unit = "ft_load_flags" },
|
yading@10
|
212 { "force_autohint", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_FORCE_AUTOHINT }, .flags = FLAGS, .unit = "ft_load_flags" },
|
yading@10
|
213 { "crop_bitmap", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_CROP_BITMAP }, .flags = FLAGS, .unit = "ft_load_flags" },
|
yading@10
|
214 { "pedantic", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_PEDANTIC }, .flags = FLAGS, .unit = "ft_load_flags" },
|
yading@10
|
215 { "ignore_global_advance_width", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH }, .flags = FLAGS, .unit = "ft_load_flags" },
|
yading@10
|
216 { "no_recurse", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_RECURSE }, .flags = FLAGS, .unit = "ft_load_flags" },
|
yading@10
|
217 { "ignore_transform", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_IGNORE_TRANSFORM }, .flags = FLAGS, .unit = "ft_load_flags" },
|
yading@10
|
218 { "monochrome", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_MONOCHROME }, .flags = FLAGS, .unit = "ft_load_flags" },
|
yading@10
|
219 { "linear_design", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_LINEAR_DESIGN }, .flags = FLAGS, .unit = "ft_load_flags" },
|
yading@10
|
220 { "no_autohint", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_AUTOHINT }, .flags = FLAGS, .unit = "ft_load_flags" },
|
yading@10
|
221 { NULL},
|
yading@10
|
222 };
|
yading@10
|
223
|
yading@10
|
224 AVFILTER_DEFINE_CLASS(drawtext);
|
yading@10
|
225
|
yading@10
|
226 #undef __FTERRORS_H__
|
yading@10
|
227 #define FT_ERROR_START_LIST {
|
yading@10
|
228 #define FT_ERRORDEF(e, v, s) { (e), (s) },
|
yading@10
|
229 #define FT_ERROR_END_LIST { 0, NULL } };
|
yading@10
|
230
|
yading@10
|
231 struct ft_error
|
yading@10
|
232 {
|
yading@10
|
233 int err;
|
yading@10
|
234 const char *err_msg;
|
yading@10
|
235 } static ft_errors[] =
|
yading@10
|
236 #include FT_ERRORS_H
|
yading@10
|
237
|
yading@10
|
238 #define FT_ERRMSG(e) ft_errors[e].err_msg
|
yading@10
|
239
|
yading@10
|
240 typedef struct {
|
yading@10
|
241 FT_Glyph *glyph;
|
yading@10
|
242 uint32_t code;
|
yading@10
|
243 FT_Bitmap bitmap; ///< array holding bitmaps of font
|
yading@10
|
244 FT_BBox bbox;
|
yading@10
|
245 int advance;
|
yading@10
|
246 int bitmap_left;
|
yading@10
|
247 int bitmap_top;
|
yading@10
|
248 } Glyph;
|
yading@10
|
249
|
yading@10
|
250 static int glyph_cmp(void *key, const void *b)
|
yading@10
|
251 {
|
yading@10
|
252 const Glyph *a = key, *bb = b;
|
yading@10
|
253 int64_t diff = (int64_t)a->code - (int64_t)bb->code;
|
yading@10
|
254 return diff > 0 ? 1 : diff < 0 ? -1 : 0;
|
yading@10
|
255 }
|
yading@10
|
256
|
yading@10
|
257 /**
|
yading@10
|
258 * Load glyphs corresponding to the UTF-32 codepoint code.
|
yading@10
|
259 */
|
yading@10
|
260 static int load_glyph(AVFilterContext *ctx, Glyph **glyph_ptr, uint32_t code)
|
yading@10
|
261 {
|
yading@10
|
262 DrawTextContext *dtext = ctx->priv;
|
yading@10
|
263 Glyph *glyph;
|
yading@10
|
264 struct AVTreeNode *node = NULL;
|
yading@10
|
265 int ret;
|
yading@10
|
266
|
yading@10
|
267 /* load glyph into dtext->face->glyph */
|
yading@10
|
268 if (FT_Load_Char(dtext->face, code, dtext->ft_load_flags))
|
yading@10
|
269 return AVERROR(EINVAL);
|
yading@10
|
270
|
yading@10
|
271 /* save glyph */
|
yading@10
|
272 if (!(glyph = av_mallocz(sizeof(*glyph))) ||
|
yading@10
|
273 !(glyph->glyph = av_mallocz(sizeof(*glyph->glyph)))) {
|
yading@10
|
274 ret = AVERROR(ENOMEM);
|
yading@10
|
275 goto error;
|
yading@10
|
276 }
|
yading@10
|
277 glyph->code = code;
|
yading@10
|
278
|
yading@10
|
279 if (FT_Get_Glyph(dtext->face->glyph, glyph->glyph)) {
|
yading@10
|
280 ret = AVERROR(EINVAL);
|
yading@10
|
281 goto error;
|
yading@10
|
282 }
|
yading@10
|
283
|
yading@10
|
284 glyph->bitmap = dtext->face->glyph->bitmap;
|
yading@10
|
285 glyph->bitmap_left = dtext->face->glyph->bitmap_left;
|
yading@10
|
286 glyph->bitmap_top = dtext->face->glyph->bitmap_top;
|
yading@10
|
287 glyph->advance = dtext->face->glyph->advance.x >> 6;
|
yading@10
|
288
|
yading@10
|
289 /* measure text height to calculate text_height (or the maximum text height) */
|
yading@10
|
290 FT_Glyph_Get_CBox(*glyph->glyph, ft_glyph_bbox_pixels, &glyph->bbox);
|
yading@10
|
291
|
yading@10
|
292 /* cache the newly created glyph */
|
yading@10
|
293 if (!(node = av_tree_node_alloc())) {
|
yading@10
|
294 ret = AVERROR(ENOMEM);
|
yading@10
|
295 goto error;
|
yading@10
|
296 }
|
yading@10
|
297 av_tree_insert(&dtext->glyphs, glyph, glyph_cmp, &node);
|
yading@10
|
298
|
yading@10
|
299 if (glyph_ptr)
|
yading@10
|
300 *glyph_ptr = glyph;
|
yading@10
|
301 return 0;
|
yading@10
|
302
|
yading@10
|
303 error:
|
yading@10
|
304 if (glyph)
|
yading@10
|
305 av_freep(&glyph->glyph);
|
yading@10
|
306 av_freep(&glyph);
|
yading@10
|
307 av_freep(&node);
|
yading@10
|
308 return ret;
|
yading@10
|
309 }
|
yading@10
|
310
|
yading@10
|
311 static int load_font_file(AVFilterContext *ctx, const char *path, int index,
|
yading@10
|
312 const char **error)
|
yading@10
|
313 {
|
yading@10
|
314 DrawTextContext *dtext = ctx->priv;
|
yading@10
|
315 int err;
|
yading@10
|
316
|
yading@10
|
317 err = FT_New_Face(dtext->library, path, index, &dtext->face);
|
yading@10
|
318 if (err) {
|
yading@10
|
319 *error = FT_ERRMSG(err);
|
yading@10
|
320 return AVERROR(EINVAL);
|
yading@10
|
321 }
|
yading@10
|
322 return 0;
|
yading@10
|
323 }
|
yading@10
|
324
|
yading@10
|
325 #if CONFIG_FONTCONFIG
|
yading@10
|
326 static int load_font_fontconfig(AVFilterContext *ctx, const char **error)
|
yading@10
|
327 {
|
yading@10
|
328 DrawTextContext *dtext = ctx->priv;
|
yading@10
|
329 FcConfig *fontconfig;
|
yading@10
|
330 FcPattern *pattern, *fpat;
|
yading@10
|
331 FcResult result = FcResultMatch;
|
yading@10
|
332 FcChar8 *filename;
|
yading@10
|
333 int err, index;
|
yading@10
|
334 double size;
|
yading@10
|
335
|
yading@10
|
336 fontconfig = FcInitLoadConfigAndFonts();
|
yading@10
|
337 if (!fontconfig) {
|
yading@10
|
338 *error = "impossible to init fontconfig\n";
|
yading@10
|
339 return AVERROR(EINVAL);
|
yading@10
|
340 }
|
yading@10
|
341 pattern = FcNameParse(dtext->fontfile ? dtext->fontfile :
|
yading@10
|
342 (uint8_t *)(intptr_t)"default");
|
yading@10
|
343 if (!pattern) {
|
yading@10
|
344 *error = "could not parse fontconfig pattern";
|
yading@10
|
345 return AVERROR(EINVAL);
|
yading@10
|
346 }
|
yading@10
|
347 if (!FcConfigSubstitute(fontconfig, pattern, FcMatchPattern)) {
|
yading@10
|
348 *error = "could not substitue fontconfig options"; /* very unlikely */
|
yading@10
|
349 return AVERROR(EINVAL);
|
yading@10
|
350 }
|
yading@10
|
351 FcDefaultSubstitute(pattern);
|
yading@10
|
352 fpat = FcFontMatch(fontconfig, pattern, &result);
|
yading@10
|
353 if (!fpat || result != FcResultMatch) {
|
yading@10
|
354 *error = "impossible to find a matching font";
|
yading@10
|
355 return AVERROR(EINVAL);
|
yading@10
|
356 }
|
yading@10
|
357 if (FcPatternGetString (fpat, FC_FILE, 0, &filename) != FcResultMatch ||
|
yading@10
|
358 FcPatternGetInteger(fpat, FC_INDEX, 0, &index ) != FcResultMatch ||
|
yading@10
|
359 FcPatternGetDouble (fpat, FC_SIZE, 0, &size ) != FcResultMatch) {
|
yading@10
|
360 *error = "impossible to find font information";
|
yading@10
|
361 return AVERROR(EINVAL);
|
yading@10
|
362 }
|
yading@10
|
363 av_log(ctx, AV_LOG_INFO, "Using \"%s\"\n", filename);
|
yading@10
|
364 if (!dtext->fontsize)
|
yading@10
|
365 dtext->fontsize = size + 0.5;
|
yading@10
|
366 err = load_font_file(ctx, filename, index, error);
|
yading@10
|
367 if (err)
|
yading@10
|
368 return err;
|
yading@10
|
369 FcPatternDestroy(fpat);
|
yading@10
|
370 FcPatternDestroy(pattern);
|
yading@10
|
371 FcConfigDestroy(fontconfig);
|
yading@10
|
372 return 0;
|
yading@10
|
373 }
|
yading@10
|
374 #endif
|
yading@10
|
375
|
yading@10
|
376 static int load_font(AVFilterContext *ctx)
|
yading@10
|
377 {
|
yading@10
|
378 DrawTextContext *dtext = ctx->priv;
|
yading@10
|
379 int err;
|
yading@10
|
380 const char *error = "unknown error\n";
|
yading@10
|
381
|
yading@10
|
382 /* load the face, and set up the encoding, which is by default UTF-8 */
|
yading@10
|
383 err = load_font_file(ctx, dtext->fontfile, 0, &error);
|
yading@10
|
384 if (!err)
|
yading@10
|
385 return 0;
|
yading@10
|
386 #if CONFIG_FONTCONFIG
|
yading@10
|
387 err = load_font_fontconfig(ctx, &error);
|
yading@10
|
388 if (!err)
|
yading@10
|
389 return 0;
|
yading@10
|
390 #endif
|
yading@10
|
391 av_log(ctx, AV_LOG_ERROR, "Could not load font \"%s\": %s\n",
|
yading@10
|
392 dtext->fontfile, error);
|
yading@10
|
393 return err;
|
yading@10
|
394 }
|
yading@10
|
395
|
yading@10
|
396 static int load_textfile(AVFilterContext *ctx)
|
yading@10
|
397 {
|
yading@10
|
398 DrawTextContext *dtext = ctx->priv;
|
yading@10
|
399 int err;
|
yading@10
|
400 uint8_t *textbuf;
|
yading@10
|
401 size_t textbuf_size;
|
yading@10
|
402
|
yading@10
|
403 if ((err = av_file_map(dtext->textfile, &textbuf, &textbuf_size, 0, ctx)) < 0) {
|
yading@10
|
404 av_log(ctx, AV_LOG_ERROR,
|
yading@10
|
405 "The text file '%s' could not be read or is empty\n",
|
yading@10
|
406 dtext->textfile);
|
yading@10
|
407 return err;
|
yading@10
|
408 }
|
yading@10
|
409
|
yading@10
|
410 if (!(dtext->text = av_realloc(dtext->text, textbuf_size + 1)))
|
yading@10
|
411 return AVERROR(ENOMEM);
|
yading@10
|
412 memcpy(dtext->text, textbuf, textbuf_size);
|
yading@10
|
413 dtext->text[textbuf_size] = 0;
|
yading@10
|
414 av_file_unmap(textbuf, textbuf_size);
|
yading@10
|
415
|
yading@10
|
416 return 0;
|
yading@10
|
417 }
|
yading@10
|
418
|
yading@10
|
419 static av_cold int init(AVFilterContext *ctx)
|
yading@10
|
420 {
|
yading@10
|
421 int err;
|
yading@10
|
422 DrawTextContext *dtext = ctx->priv;
|
yading@10
|
423 Glyph *glyph;
|
yading@10
|
424
|
yading@10
|
425 if (!dtext->fontfile && !CONFIG_FONTCONFIG) {
|
yading@10
|
426 av_log(ctx, AV_LOG_ERROR, "No font filename provided\n");
|
yading@10
|
427 return AVERROR(EINVAL);
|
yading@10
|
428 }
|
yading@10
|
429
|
yading@10
|
430 if (dtext->textfile) {
|
yading@10
|
431 if (dtext->text) {
|
yading@10
|
432 av_log(ctx, AV_LOG_ERROR,
|
yading@10
|
433 "Both text and text file provided. Please provide only one\n");
|
yading@10
|
434 return AVERROR(EINVAL);
|
yading@10
|
435 }
|
yading@10
|
436 if ((err = load_textfile(ctx)) < 0)
|
yading@10
|
437 return err;
|
yading@10
|
438 }
|
yading@10
|
439
|
yading@10
|
440 if (dtext->reload && !dtext->textfile)
|
yading@10
|
441 av_log(ctx, AV_LOG_WARNING, "No file to reload\n");
|
yading@10
|
442
|
yading@10
|
443 if (dtext->tc_opt_string) {
|
yading@10
|
444 int ret = av_timecode_init_from_string(&dtext->tc, dtext->tc_rate,
|
yading@10
|
445 dtext->tc_opt_string, ctx);
|
yading@10
|
446 if (ret < 0)
|
yading@10
|
447 return ret;
|
yading@10
|
448 if (dtext->tc24hmax)
|
yading@10
|
449 dtext->tc.flags |= AV_TIMECODE_FLAG_24HOURSMAX;
|
yading@10
|
450 if (!dtext->text)
|
yading@10
|
451 dtext->text = av_strdup("");
|
yading@10
|
452 }
|
yading@10
|
453
|
yading@10
|
454 if (!dtext->text) {
|
yading@10
|
455 av_log(ctx, AV_LOG_ERROR,
|
yading@10
|
456 "Either text, a valid file or a timecode must be provided\n");
|
yading@10
|
457 return AVERROR(EINVAL);
|
yading@10
|
458 }
|
yading@10
|
459
|
yading@10
|
460 if ((err = av_parse_color(dtext->fontcolor.rgba, dtext->fontcolor_string, -1, ctx))) {
|
yading@10
|
461 av_log(ctx, AV_LOG_ERROR,
|
yading@10
|
462 "Invalid font color '%s'\n", dtext->fontcolor_string);
|
yading@10
|
463 return err;
|
yading@10
|
464 }
|
yading@10
|
465
|
yading@10
|
466 if ((err = av_parse_color(dtext->boxcolor.rgba, dtext->boxcolor_string, -1, ctx))) {
|
yading@10
|
467 av_log(ctx, AV_LOG_ERROR,
|
yading@10
|
468 "Invalid box color '%s'\n", dtext->boxcolor_string);
|
yading@10
|
469 return err;
|
yading@10
|
470 }
|
yading@10
|
471
|
yading@10
|
472 if ((err = av_parse_color(dtext->shadowcolor.rgba, dtext->shadowcolor_string, -1, ctx))) {
|
yading@10
|
473 av_log(ctx, AV_LOG_ERROR,
|
yading@10
|
474 "Invalid shadow color '%s'\n", dtext->shadowcolor_string);
|
yading@10
|
475 return err;
|
yading@10
|
476 }
|
yading@10
|
477
|
yading@10
|
478 if ((err = FT_Init_FreeType(&(dtext->library)))) {
|
yading@10
|
479 av_log(ctx, AV_LOG_ERROR,
|
yading@10
|
480 "Could not load FreeType: %s\n", FT_ERRMSG(err));
|
yading@10
|
481 return AVERROR(EINVAL);
|
yading@10
|
482 }
|
yading@10
|
483
|
yading@10
|
484 err = load_font(ctx);
|
yading@10
|
485 if (err)
|
yading@10
|
486 return err;
|
yading@10
|
487 if (!dtext->fontsize)
|
yading@10
|
488 dtext->fontsize = 16;
|
yading@10
|
489 if ((err = FT_Set_Pixel_Sizes(dtext->face, 0, dtext->fontsize))) {
|
yading@10
|
490 av_log(ctx, AV_LOG_ERROR, "Could not set font size to %d pixels: %s\n",
|
yading@10
|
491 dtext->fontsize, FT_ERRMSG(err));
|
yading@10
|
492 return AVERROR(EINVAL);
|
yading@10
|
493 }
|
yading@10
|
494
|
yading@10
|
495 dtext->use_kerning = FT_HAS_KERNING(dtext->face);
|
yading@10
|
496
|
yading@10
|
497 /* load the fallback glyph with code 0 */
|
yading@10
|
498 load_glyph(ctx, NULL, 0);
|
yading@10
|
499
|
yading@10
|
500 /* set the tabsize in pixels */
|
yading@10
|
501 if ((err = load_glyph(ctx, &glyph, ' ')) < 0) {
|
yading@10
|
502 av_log(ctx, AV_LOG_ERROR, "Could not set tabsize.\n");
|
yading@10
|
503 return err;
|
yading@10
|
504 }
|
yading@10
|
505 dtext->tabsize *= glyph->advance;
|
yading@10
|
506
|
yading@10
|
507 if (dtext->exp_mode == EXP_STRFTIME &&
|
yading@10
|
508 (strchr(dtext->text, '%') || strchr(dtext->text, '\\')))
|
yading@10
|
509 av_log(ctx, AV_LOG_WARNING, "expansion=strftime is deprecated.\n");
|
yading@10
|
510
|
yading@10
|
511 av_bprint_init(&dtext->expanded_text, 0, AV_BPRINT_SIZE_UNLIMITED);
|
yading@10
|
512
|
yading@10
|
513 return 0;
|
yading@10
|
514 }
|
yading@10
|
515
|
yading@10
|
516 static int query_formats(AVFilterContext *ctx)
|
yading@10
|
517 {
|
yading@10
|
518 ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0));
|
yading@10
|
519 return 0;
|
yading@10
|
520 }
|
yading@10
|
521
|
yading@10
|
522 static int glyph_enu_free(void *opaque, void *elem)
|
yading@10
|
523 {
|
yading@10
|
524 Glyph *glyph = elem;
|
yading@10
|
525
|
yading@10
|
526 FT_Done_Glyph(*glyph->glyph);
|
yading@10
|
527 av_freep(&glyph->glyph);
|
yading@10
|
528 av_free(elem);
|
yading@10
|
529 return 0;
|
yading@10
|
530 }
|
yading@10
|
531
|
yading@10
|
532 static av_cold void uninit(AVFilterContext *ctx)
|
yading@10
|
533 {
|
yading@10
|
534 DrawTextContext *dtext = ctx->priv;
|
yading@10
|
535
|
yading@10
|
536 av_expr_free(dtext->x_pexpr); dtext->x_pexpr = NULL;
|
yading@10
|
537 av_expr_free(dtext->y_pexpr); dtext->y_pexpr = NULL;
|
yading@10
|
538 av_expr_free(dtext->draw_pexpr); dtext->draw_pexpr = NULL;
|
yading@10
|
539
|
yading@10
|
540 av_freep(&dtext->positions);
|
yading@10
|
541 dtext->nb_positions = 0;
|
yading@10
|
542
|
yading@10
|
543
|
yading@10
|
544 av_tree_enumerate(dtext->glyphs, NULL, NULL, glyph_enu_free);
|
yading@10
|
545 av_tree_destroy(dtext->glyphs);
|
yading@10
|
546 dtext->glyphs = NULL;
|
yading@10
|
547
|
yading@10
|
548 FT_Done_Face(dtext->face);
|
yading@10
|
549 FT_Done_FreeType(dtext->library);
|
yading@10
|
550
|
yading@10
|
551 av_bprint_finalize(&dtext->expanded_text, NULL);
|
yading@10
|
552 }
|
yading@10
|
553
|
yading@10
|
554 static inline int is_newline(uint32_t c)
|
yading@10
|
555 {
|
yading@10
|
556 return c == '\n' || c == '\r' || c == '\f' || c == '\v';
|
yading@10
|
557 }
|
yading@10
|
558
|
yading@10
|
559 static int config_input(AVFilterLink *inlink)
|
yading@10
|
560 {
|
yading@10
|
561 AVFilterContext *ctx = inlink->dst;
|
yading@10
|
562 DrawTextContext *dtext = ctx->priv;
|
yading@10
|
563 int ret;
|
yading@10
|
564
|
yading@10
|
565 ff_draw_init(&dtext->dc, inlink->format, 0);
|
yading@10
|
566 ff_draw_color(&dtext->dc, &dtext->fontcolor, dtext->fontcolor.rgba);
|
yading@10
|
567 ff_draw_color(&dtext->dc, &dtext->shadowcolor, dtext->shadowcolor.rgba);
|
yading@10
|
568 ff_draw_color(&dtext->dc, &dtext->boxcolor, dtext->boxcolor.rgba);
|
yading@10
|
569
|
yading@10
|
570 dtext->var_values[VAR_w] = dtext->var_values[VAR_W] = dtext->var_values[VAR_MAIN_W] = inlink->w;
|
yading@10
|
571 dtext->var_values[VAR_h] = dtext->var_values[VAR_H] = dtext->var_values[VAR_MAIN_H] = inlink->h;
|
yading@10
|
572 dtext->var_values[VAR_SAR] = inlink->sample_aspect_ratio.num ? av_q2d(inlink->sample_aspect_ratio) : 1;
|
yading@10
|
573 dtext->var_values[VAR_DAR] = (double)inlink->w / inlink->h * dtext->var_values[VAR_SAR];
|
yading@10
|
574 dtext->var_values[VAR_HSUB] = 1 << dtext->dc.hsub_max;
|
yading@10
|
575 dtext->var_values[VAR_VSUB] = 1 << dtext->dc.vsub_max;
|
yading@10
|
576 dtext->var_values[VAR_X] = NAN;
|
yading@10
|
577 dtext->var_values[VAR_Y] = NAN;
|
yading@10
|
578 if (!dtext->reinit)
|
yading@10
|
579 dtext->var_values[VAR_N] = 0;
|
yading@10
|
580 dtext->var_values[VAR_T] = NAN;
|
yading@10
|
581
|
yading@10
|
582 av_lfg_init(&dtext->prng, av_get_random_seed());
|
yading@10
|
583
|
yading@10
|
584 if ((ret = av_expr_parse(&dtext->x_pexpr, dtext->x_expr, var_names,
|
yading@10
|
585 NULL, NULL, fun2_names, fun2, 0, ctx)) < 0 ||
|
yading@10
|
586 (ret = av_expr_parse(&dtext->y_pexpr, dtext->y_expr, var_names,
|
yading@10
|
587 NULL, NULL, fun2_names, fun2, 0, ctx)) < 0 ||
|
yading@10
|
588 (ret = av_expr_parse(&dtext->draw_pexpr, dtext->draw_expr, var_names,
|
yading@10
|
589 NULL, NULL, fun2_names, fun2, 0, ctx)) < 0)
|
yading@10
|
590
|
yading@10
|
591 return AVERROR(EINVAL);
|
yading@10
|
592
|
yading@10
|
593 return 0;
|
yading@10
|
594 }
|
yading@10
|
595
|
yading@10
|
596 static int command(AVFilterContext *ctx, const char *cmd, const char *arg, char *res, int res_len, int flags)
|
yading@10
|
597 {
|
yading@10
|
598 DrawTextContext *dtext = ctx->priv;
|
yading@10
|
599
|
yading@10
|
600 if (!strcmp(cmd, "reinit")) {
|
yading@10
|
601 int ret;
|
yading@10
|
602 uninit(ctx);
|
yading@10
|
603 dtext->reinit = 1;
|
yading@10
|
604 if ((ret = init(ctx)) < 0)
|
yading@10
|
605 return ret;
|
yading@10
|
606 return config_input(ctx->inputs[0]);
|
yading@10
|
607 }
|
yading@10
|
608
|
yading@10
|
609 return AVERROR(ENOSYS);
|
yading@10
|
610 }
|
yading@10
|
611
|
yading@10
|
612 static int func_pts(AVFilterContext *ctx, AVBPrint *bp,
|
yading@10
|
613 char *fct, unsigned argc, char **argv, int tag)
|
yading@10
|
614 {
|
yading@10
|
615 DrawTextContext *dtext = ctx->priv;
|
yading@10
|
616
|
yading@10
|
617 av_bprintf(bp, "%.6f", dtext->var_values[VAR_T]);
|
yading@10
|
618 return 0;
|
yading@10
|
619 }
|
yading@10
|
620
|
yading@10
|
621 static int func_frame_num(AVFilterContext *ctx, AVBPrint *bp,
|
yading@10
|
622 char *fct, unsigned argc, char **argv, int tag)
|
yading@10
|
623 {
|
yading@10
|
624 DrawTextContext *dtext = ctx->priv;
|
yading@10
|
625
|
yading@10
|
626 av_bprintf(bp, "%d", (int)dtext->var_values[VAR_N]);
|
yading@10
|
627 return 0;
|
yading@10
|
628 }
|
yading@10
|
629
|
yading@10
|
630 #if !HAVE_LOCALTIME_R
|
yading@10
|
631 static void localtime_r(const time_t *t, struct tm *tm)
|
yading@10
|
632 {
|
yading@10
|
633 *tm = *localtime(t);
|
yading@10
|
634 }
|
yading@10
|
635 #endif
|
yading@10
|
636
|
yading@10
|
637 static int func_strftime(AVFilterContext *ctx, AVBPrint *bp,
|
yading@10
|
638 char *fct, unsigned argc, char **argv, int tag)
|
yading@10
|
639 {
|
yading@10
|
640 const char *fmt = argc ? argv[0] : "%Y-%m-%d %H:%M:%S";
|
yading@10
|
641 time_t now;
|
yading@10
|
642 struct tm tm;
|
yading@10
|
643
|
yading@10
|
644 time(&now);
|
yading@10
|
645 if (tag == 'L')
|
yading@10
|
646 localtime_r(&now, &tm);
|
yading@10
|
647 else
|
yading@10
|
648 tm = *gmtime(&now);
|
yading@10
|
649 av_bprint_strftime(bp, fmt, &tm);
|
yading@10
|
650 return 0;
|
yading@10
|
651 }
|
yading@10
|
652
|
yading@10
|
653 static int func_eval_expr(AVFilterContext *ctx, AVBPrint *bp,
|
yading@10
|
654 char *fct, unsigned argc, char **argv, int tag)
|
yading@10
|
655 {
|
yading@10
|
656 DrawTextContext *dtext = ctx->priv;
|
yading@10
|
657 double res;
|
yading@10
|
658 int ret;
|
yading@10
|
659
|
yading@10
|
660 ret = av_expr_parse_and_eval(&res, argv[0], var_names, dtext->var_values,
|
yading@10
|
661 NULL, NULL, fun2_names, fun2,
|
yading@10
|
662 &dtext->prng, 0, ctx);
|
yading@10
|
663 if (ret < 0)
|
yading@10
|
664 av_log(ctx, AV_LOG_ERROR,
|
yading@10
|
665 "Expression '%s' for the expr text expansion function is not valid\n",
|
yading@10
|
666 argv[0]);
|
yading@10
|
667 else
|
yading@10
|
668 av_bprintf(bp, "%f", res);
|
yading@10
|
669
|
yading@10
|
670 return ret;
|
yading@10
|
671 }
|
yading@10
|
672
|
yading@10
|
673 static const struct drawtext_function {
|
yading@10
|
674 const char *name;
|
yading@10
|
675 unsigned argc_min, argc_max;
|
yading@10
|
676 int tag; /** opaque argument to func */
|
yading@10
|
677 int (*func)(AVFilterContext *, AVBPrint *, char *, unsigned, char **, int);
|
yading@10
|
678 } functions[] = {
|
yading@10
|
679 { "expr", 1, 1, 0, func_eval_expr },
|
yading@10
|
680 { "e", 1, 1, 0, func_eval_expr },
|
yading@10
|
681 { "pts", 0, 0, 0, func_pts },
|
yading@10
|
682 { "gmtime", 0, 1, 'G', func_strftime },
|
yading@10
|
683 { "localtime", 0, 1, 'L', func_strftime },
|
yading@10
|
684 { "frame_num", 0, 0, 0, func_frame_num },
|
yading@10
|
685 { "n", 0, 0, 0, func_frame_num },
|
yading@10
|
686 };
|
yading@10
|
687
|
yading@10
|
688 static int eval_function(AVFilterContext *ctx, AVBPrint *bp, char *fct,
|
yading@10
|
689 unsigned argc, char **argv)
|
yading@10
|
690 {
|
yading@10
|
691 unsigned i;
|
yading@10
|
692
|
yading@10
|
693 for (i = 0; i < FF_ARRAY_ELEMS(functions); i++) {
|
yading@10
|
694 if (strcmp(fct, functions[i].name))
|
yading@10
|
695 continue;
|
yading@10
|
696 if (argc < functions[i].argc_min) {
|
yading@10
|
697 av_log(ctx, AV_LOG_ERROR, "%%{%s} requires at least %d arguments\n",
|
yading@10
|
698 fct, functions[i].argc_min);
|
yading@10
|
699 return AVERROR(EINVAL);
|
yading@10
|
700 }
|
yading@10
|
701 if (argc > functions[i].argc_max) {
|
yading@10
|
702 av_log(ctx, AV_LOG_ERROR, "%%{%s} requires at most %d arguments\n",
|
yading@10
|
703 fct, functions[i].argc_max);
|
yading@10
|
704 return AVERROR(EINVAL);
|
yading@10
|
705 }
|
yading@10
|
706 break;
|
yading@10
|
707 }
|
yading@10
|
708 if (i >= FF_ARRAY_ELEMS(functions)) {
|
yading@10
|
709 av_log(ctx, AV_LOG_ERROR, "%%{%s} is not known\n", fct);
|
yading@10
|
710 return AVERROR(EINVAL);
|
yading@10
|
711 }
|
yading@10
|
712 return functions[i].func(ctx, bp, fct, argc, argv, functions[i].tag);
|
yading@10
|
713 }
|
yading@10
|
714
|
yading@10
|
715 static int expand_function(AVFilterContext *ctx, AVBPrint *bp, char **rtext)
|
yading@10
|
716 {
|
yading@10
|
717 const char *text = *rtext;
|
yading@10
|
718 char *argv[16] = { NULL };
|
yading@10
|
719 unsigned argc = 0, i;
|
yading@10
|
720 int ret;
|
yading@10
|
721
|
yading@10
|
722 if (*text != '{') {
|
yading@10
|
723 av_log(ctx, AV_LOG_ERROR, "Stray %% near '%s'\n", text);
|
yading@10
|
724 return AVERROR(EINVAL);
|
yading@10
|
725 }
|
yading@10
|
726 text++;
|
yading@10
|
727 while (1) {
|
yading@10
|
728 if (!(argv[argc++] = av_get_token(&text, ":}"))) {
|
yading@10
|
729 ret = AVERROR(ENOMEM);
|
yading@10
|
730 goto end;
|
yading@10
|
731 }
|
yading@10
|
732 if (!*text) {
|
yading@10
|
733 av_log(ctx, AV_LOG_ERROR, "Unterminated %%{} near '%s'\n", *rtext);
|
yading@10
|
734 ret = AVERROR(EINVAL);
|
yading@10
|
735 goto end;
|
yading@10
|
736 }
|
yading@10
|
737 if (argc == FF_ARRAY_ELEMS(argv))
|
yading@10
|
738 av_freep(&argv[--argc]); /* error will be caught later */
|
yading@10
|
739 if (*text == '}')
|
yading@10
|
740 break;
|
yading@10
|
741 text++;
|
yading@10
|
742 }
|
yading@10
|
743
|
yading@10
|
744 if ((ret = eval_function(ctx, bp, argv[0], argc - 1, argv + 1)) < 0)
|
yading@10
|
745 goto end;
|
yading@10
|
746 ret = 0;
|
yading@10
|
747 *rtext = (char *)text + 1;
|
yading@10
|
748
|
yading@10
|
749 end:
|
yading@10
|
750 for (i = 0; i < argc; i++)
|
yading@10
|
751 av_freep(&argv[i]);
|
yading@10
|
752 return ret;
|
yading@10
|
753 }
|
yading@10
|
754
|
yading@10
|
755 static int expand_text(AVFilterContext *ctx)
|
yading@10
|
756 {
|
yading@10
|
757 DrawTextContext *dtext = ctx->priv;
|
yading@10
|
758 char *text = dtext->text;
|
yading@10
|
759 AVBPrint *bp = &dtext->expanded_text;
|
yading@10
|
760 int ret;
|
yading@10
|
761
|
yading@10
|
762 av_bprint_clear(bp);
|
yading@10
|
763 while (*text) {
|
yading@10
|
764 if (*text == '\\' && text[1]) {
|
yading@10
|
765 av_bprint_chars(bp, text[1], 1);
|
yading@10
|
766 text += 2;
|
yading@10
|
767 } else if (*text == '%') {
|
yading@10
|
768 text++;
|
yading@10
|
769 if ((ret = expand_function(ctx, bp, &text)) < 0)
|
yading@10
|
770 return ret;
|
yading@10
|
771 } else {
|
yading@10
|
772 av_bprint_chars(bp, *text, 1);
|
yading@10
|
773 text++;
|
yading@10
|
774 }
|
yading@10
|
775 }
|
yading@10
|
776 if (!av_bprint_is_complete(bp))
|
yading@10
|
777 return AVERROR(ENOMEM);
|
yading@10
|
778 return 0;
|
yading@10
|
779 }
|
yading@10
|
780
|
yading@10
|
781 static int draw_glyphs(DrawTextContext *dtext, AVFrame *frame,
|
yading@10
|
782 int width, int height, const uint8_t rgbcolor[4], FFDrawColor *color, int x, int y)
|
yading@10
|
783 {
|
yading@10
|
784 char *text = dtext->expanded_text.str;
|
yading@10
|
785 uint32_t code = 0;
|
yading@10
|
786 int i, x1, y1;
|
yading@10
|
787 uint8_t *p;
|
yading@10
|
788 Glyph *glyph = NULL;
|
yading@10
|
789
|
yading@10
|
790 for (i = 0, p = text; *p; i++) {
|
yading@10
|
791 Glyph dummy = { 0 };
|
yading@10
|
792 GET_UTF8(code, *p++, continue;);
|
yading@10
|
793
|
yading@10
|
794 /* skip new line chars, just go to new line */
|
yading@10
|
795 if (code == '\n' || code == '\r' || code == '\t')
|
yading@10
|
796 continue;
|
yading@10
|
797
|
yading@10
|
798 dummy.code = code;
|
yading@10
|
799 glyph = av_tree_find(dtext->glyphs, &dummy, (void *)glyph_cmp, NULL);
|
yading@10
|
800
|
yading@10
|
801 if (glyph->bitmap.pixel_mode != FT_PIXEL_MODE_MONO &&
|
yading@10
|
802 glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
|
yading@10
|
803 return AVERROR(EINVAL);
|
yading@10
|
804
|
yading@10
|
805 x1 = dtext->positions[i].x+dtext->x+x;
|
yading@10
|
806 y1 = dtext->positions[i].y+dtext->y+y;
|
yading@10
|
807
|
yading@10
|
808 ff_blend_mask(&dtext->dc, color,
|
yading@10
|
809 frame->data, frame->linesize, width, height,
|
yading@10
|
810 glyph->bitmap.buffer, glyph->bitmap.pitch,
|
yading@10
|
811 glyph->bitmap.width, glyph->bitmap.rows,
|
yading@10
|
812 glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO ? 0 : 3,
|
yading@10
|
813 0, x1, y1);
|
yading@10
|
814 }
|
yading@10
|
815
|
yading@10
|
816 return 0;
|
yading@10
|
817 }
|
yading@10
|
818
|
yading@10
|
819 static int draw_text(AVFilterContext *ctx, AVFrame *frame,
|
yading@10
|
820 int width, int height)
|
yading@10
|
821 {
|
yading@10
|
822 DrawTextContext *dtext = ctx->priv;
|
yading@10
|
823 uint32_t code = 0, prev_code = 0;
|
yading@10
|
824 int x = 0, y = 0, i = 0, ret;
|
yading@10
|
825 int max_text_line_w = 0, len;
|
yading@10
|
826 int box_w, box_h;
|
yading@10
|
827 char *text = dtext->text;
|
yading@10
|
828 uint8_t *p;
|
yading@10
|
829 int y_min = 32000, y_max = -32000;
|
yading@10
|
830 int x_min = 32000, x_max = -32000;
|
yading@10
|
831 FT_Vector delta;
|
yading@10
|
832 Glyph *glyph = NULL, *prev_glyph = NULL;
|
yading@10
|
833 Glyph dummy = { 0 };
|
yading@10
|
834
|
yading@10
|
835 time_t now = time(0);
|
yading@10
|
836 struct tm ltime;
|
yading@10
|
837 AVBPrint *bp = &dtext->expanded_text;
|
yading@10
|
838
|
yading@10
|
839 av_bprint_clear(bp);
|
yading@10
|
840
|
yading@10
|
841 if(dtext->basetime != AV_NOPTS_VALUE)
|
yading@10
|
842 now= frame->pts*av_q2d(ctx->inputs[0]->time_base) + dtext->basetime/1000000;
|
yading@10
|
843
|
yading@10
|
844 switch (dtext->exp_mode) {
|
yading@10
|
845 case EXP_NONE:
|
yading@10
|
846 av_bprintf(bp, "%s", dtext->text);
|
yading@10
|
847 break;
|
yading@10
|
848 case EXP_NORMAL:
|
yading@10
|
849 if ((ret = expand_text(ctx)) < 0)
|
yading@10
|
850 return ret;
|
yading@10
|
851 break;
|
yading@10
|
852 case EXP_STRFTIME:
|
yading@10
|
853 localtime_r(&now, <ime);
|
yading@10
|
854 av_bprint_strftime(bp, dtext->text, <ime);
|
yading@10
|
855 break;
|
yading@10
|
856 }
|
yading@10
|
857
|
yading@10
|
858 if (dtext->tc_opt_string) {
|
yading@10
|
859 char tcbuf[AV_TIMECODE_STR_SIZE];
|
yading@10
|
860 av_timecode_make_string(&dtext->tc, tcbuf, dtext->frame_id++);
|
yading@10
|
861 av_bprint_clear(bp);
|
yading@10
|
862 av_bprintf(bp, "%s%s", dtext->text, tcbuf);
|
yading@10
|
863 }
|
yading@10
|
864
|
yading@10
|
865 if (!av_bprint_is_complete(bp))
|
yading@10
|
866 return AVERROR(ENOMEM);
|
yading@10
|
867 text = dtext->expanded_text.str;
|
yading@10
|
868 if ((len = dtext->expanded_text.len) > dtext->nb_positions) {
|
yading@10
|
869 if (!(dtext->positions =
|
yading@10
|
870 av_realloc(dtext->positions, len*sizeof(*dtext->positions))))
|
yading@10
|
871 return AVERROR(ENOMEM);
|
yading@10
|
872 dtext->nb_positions = len;
|
yading@10
|
873 }
|
yading@10
|
874
|
yading@10
|
875 x = 0;
|
yading@10
|
876 y = 0;
|
yading@10
|
877
|
yading@10
|
878 /* load and cache glyphs */
|
yading@10
|
879 for (i = 0, p = text; *p; i++) {
|
yading@10
|
880 GET_UTF8(code, *p++, continue;);
|
yading@10
|
881
|
yading@10
|
882 /* get glyph */
|
yading@10
|
883 dummy.code = code;
|
yading@10
|
884 glyph = av_tree_find(dtext->glyphs, &dummy, glyph_cmp, NULL);
|
yading@10
|
885 if (!glyph) {
|
yading@10
|
886 load_glyph(ctx, &glyph, code);
|
yading@10
|
887 }
|
yading@10
|
888
|
yading@10
|
889 y_min = FFMIN(glyph->bbox.yMin, y_min);
|
yading@10
|
890 y_max = FFMAX(glyph->bbox.yMax, y_max);
|
yading@10
|
891 x_min = FFMIN(glyph->bbox.xMin, x_min);
|
yading@10
|
892 x_max = FFMAX(glyph->bbox.xMax, x_max);
|
yading@10
|
893 }
|
yading@10
|
894 dtext->max_glyph_h = y_max - y_min;
|
yading@10
|
895 dtext->max_glyph_w = x_max - x_min;
|
yading@10
|
896
|
yading@10
|
897 /* compute and save position for each glyph */
|
yading@10
|
898 glyph = NULL;
|
yading@10
|
899 for (i = 0, p = text; *p; i++) {
|
yading@10
|
900 GET_UTF8(code, *p++, continue;);
|
yading@10
|
901
|
yading@10
|
902 /* skip the \n in the sequence \r\n */
|
yading@10
|
903 if (prev_code == '\r' && code == '\n')
|
yading@10
|
904 continue;
|
yading@10
|
905
|
yading@10
|
906 prev_code = code;
|
yading@10
|
907 if (is_newline(code)) {
|
yading@10
|
908 max_text_line_w = FFMAX(max_text_line_w, x);
|
yading@10
|
909 y += dtext->max_glyph_h;
|
yading@10
|
910 x = 0;
|
yading@10
|
911 continue;
|
yading@10
|
912 }
|
yading@10
|
913
|
yading@10
|
914 /* get glyph */
|
yading@10
|
915 prev_glyph = glyph;
|
yading@10
|
916 dummy.code = code;
|
yading@10
|
917 glyph = av_tree_find(dtext->glyphs, &dummy, glyph_cmp, NULL);
|
yading@10
|
918
|
yading@10
|
919 /* kerning */
|
yading@10
|
920 if (dtext->use_kerning && prev_glyph && glyph->code) {
|
yading@10
|
921 FT_Get_Kerning(dtext->face, prev_glyph->code, glyph->code,
|
yading@10
|
922 ft_kerning_default, &delta);
|
yading@10
|
923 x += delta.x >> 6;
|
yading@10
|
924 }
|
yading@10
|
925
|
yading@10
|
926 /* save position */
|
yading@10
|
927 dtext->positions[i].x = x + glyph->bitmap_left;
|
yading@10
|
928 dtext->positions[i].y = y - glyph->bitmap_top + y_max;
|
yading@10
|
929 if (code == '\t') x = (x / dtext->tabsize + 1)*dtext->tabsize;
|
yading@10
|
930 else x += glyph->advance;
|
yading@10
|
931 }
|
yading@10
|
932
|
yading@10
|
933 max_text_line_w = FFMAX(x, max_text_line_w);
|
yading@10
|
934
|
yading@10
|
935 dtext->var_values[VAR_TW] = dtext->var_values[VAR_TEXT_W] = max_text_line_w;
|
yading@10
|
936 dtext->var_values[VAR_TH] = dtext->var_values[VAR_TEXT_H] = y + dtext->max_glyph_h;
|
yading@10
|
937
|
yading@10
|
938 dtext->var_values[VAR_MAX_GLYPH_W] = dtext->max_glyph_w;
|
yading@10
|
939 dtext->var_values[VAR_MAX_GLYPH_H] = dtext->max_glyph_h;
|
yading@10
|
940 dtext->var_values[VAR_MAX_GLYPH_A] = dtext->var_values[VAR_ASCENT ] = y_max;
|
yading@10
|
941 dtext->var_values[VAR_MAX_GLYPH_D] = dtext->var_values[VAR_DESCENT] = y_min;
|
yading@10
|
942
|
yading@10
|
943 dtext->var_values[VAR_LINE_H] = dtext->var_values[VAR_LH] = dtext->max_glyph_h;
|
yading@10
|
944
|
yading@10
|
945 dtext->x = dtext->var_values[VAR_X] = av_expr_eval(dtext->x_pexpr, dtext->var_values, &dtext->prng);
|
yading@10
|
946 dtext->y = dtext->var_values[VAR_Y] = av_expr_eval(dtext->y_pexpr, dtext->var_values, &dtext->prng);
|
yading@10
|
947 dtext->x = dtext->var_values[VAR_X] = av_expr_eval(dtext->x_pexpr, dtext->var_values, &dtext->prng);
|
yading@10
|
948 dtext->draw = av_expr_eval(dtext->draw_pexpr, dtext->var_values, &dtext->prng);
|
yading@10
|
949
|
yading@10
|
950 if(!dtext->draw)
|
yading@10
|
951 return 0;
|
yading@10
|
952
|
yading@10
|
953 box_w = FFMIN(width - 1 , max_text_line_w);
|
yading@10
|
954 box_h = FFMIN(height - 1, y + dtext->max_glyph_h);
|
yading@10
|
955
|
yading@10
|
956 /* draw box */
|
yading@10
|
957 if (dtext->draw_box)
|
yading@10
|
958 ff_blend_rectangle(&dtext->dc, &dtext->boxcolor,
|
yading@10
|
959 frame->data, frame->linesize, width, height,
|
yading@10
|
960 dtext->x, dtext->y, box_w, box_h);
|
yading@10
|
961
|
yading@10
|
962 if (dtext->shadowx || dtext->shadowy) {
|
yading@10
|
963 if ((ret = draw_glyphs(dtext, frame, width, height, dtext->shadowcolor.rgba,
|
yading@10
|
964 &dtext->shadowcolor, dtext->shadowx, dtext->shadowy)) < 0)
|
yading@10
|
965 return ret;
|
yading@10
|
966 }
|
yading@10
|
967
|
yading@10
|
968 if ((ret = draw_glyphs(dtext, frame, width, height, dtext->fontcolor.rgba,
|
yading@10
|
969 &dtext->fontcolor, 0, 0)) < 0)
|
yading@10
|
970 return ret;
|
yading@10
|
971
|
yading@10
|
972 return 0;
|
yading@10
|
973 }
|
yading@10
|
974
|
yading@10
|
975 static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
|
yading@10
|
976 {
|
yading@10
|
977 AVFilterContext *ctx = inlink->dst;
|
yading@10
|
978 AVFilterLink *outlink = ctx->outputs[0];
|
yading@10
|
979 DrawTextContext *dtext = ctx->priv;
|
yading@10
|
980 int ret;
|
yading@10
|
981
|
yading@10
|
982 if (dtext->reload)
|
yading@10
|
983 if ((ret = load_textfile(ctx)) < 0)
|
yading@10
|
984 return ret;
|
yading@10
|
985
|
yading@10
|
986 dtext->var_values[VAR_T] = frame->pts == AV_NOPTS_VALUE ?
|
yading@10
|
987 NAN : frame->pts * av_q2d(inlink->time_base);
|
yading@10
|
988
|
yading@10
|
989 draw_text(ctx, frame, frame->width, frame->height);
|
yading@10
|
990
|
yading@10
|
991 av_log(ctx, AV_LOG_DEBUG, "n:%d t:%f text_w:%d text_h:%d x:%d y:%d\n",
|
yading@10
|
992 (int)dtext->var_values[VAR_N], dtext->var_values[VAR_T],
|
yading@10
|
993 (int)dtext->var_values[VAR_TEXT_W], (int)dtext->var_values[VAR_TEXT_H],
|
yading@10
|
994 dtext->x, dtext->y);
|
yading@10
|
995
|
yading@10
|
996 dtext->var_values[VAR_N] += 1.0;
|
yading@10
|
997
|
yading@10
|
998 return ff_filter_frame(outlink, frame);
|
yading@10
|
999 }
|
yading@10
|
1000
|
yading@10
|
1001 static const AVFilterPad avfilter_vf_drawtext_inputs[] = {
|
yading@10
|
1002 {
|
yading@10
|
1003 .name = "default",
|
yading@10
|
1004 .type = AVMEDIA_TYPE_VIDEO,
|
yading@10
|
1005 .get_video_buffer = ff_null_get_video_buffer,
|
yading@10
|
1006 .filter_frame = filter_frame,
|
yading@10
|
1007 .config_props = config_input,
|
yading@10
|
1008 .needs_writable = 1,
|
yading@10
|
1009 },
|
yading@10
|
1010 { NULL }
|
yading@10
|
1011 };
|
yading@10
|
1012
|
yading@10
|
1013 static const AVFilterPad avfilter_vf_drawtext_outputs[] = {
|
yading@10
|
1014 {
|
yading@10
|
1015 .name = "default",
|
yading@10
|
1016 .type = AVMEDIA_TYPE_VIDEO,
|
yading@10
|
1017 },
|
yading@10
|
1018 { NULL }
|
yading@10
|
1019 };
|
yading@10
|
1020
|
yading@10
|
1021 AVFilter avfilter_vf_drawtext = {
|
yading@10
|
1022 .name = "drawtext",
|
yading@10
|
1023 .description = NULL_IF_CONFIG_SMALL("Draw text on top of video frames using libfreetype library."),
|
yading@10
|
1024 .priv_size = sizeof(DrawTextContext),
|
yading@10
|
1025 .priv_class = &drawtext_class,
|
yading@10
|
1026 .init = init,
|
yading@10
|
1027 .uninit = uninit,
|
yading@10
|
1028 .query_formats = query_formats,
|
yading@10
|
1029
|
yading@10
|
1030 .inputs = avfilter_vf_drawtext_inputs,
|
yading@10
|
1031 .outputs = avfilter_vf_drawtext_outputs,
|
yading@10
|
1032 .process_command = command,
|
yading@10
|
1033 };
|