annotate ffmpeg/libavfilter/vf_drawtext.c @ 13:844d341cf643 tip

Back up before ISMIR
author Yading Song <yading.song@eecs.qmul.ac.uk>
date Thu, 31 Oct 2013 13:17:06 +0000
parents 6840f77b83aa
children
rev   line source
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, &ltime);
yading@10 854 av_bprint_strftime(bp, dtext->text, &ltime);
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 };