00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00029 #include <sys/time.h>
00030 #include <time.h>
00031
00032 #include "config.h"
00033 #include "libavutil/avstring.h"
00034 #include "libavutil/bprint.h"
00035 #include "libavutil/common.h"
00036 #include "libavutil/file.h"
00037 #include "libavutil/eval.h"
00038 #include "libavutil/opt.h"
00039 #include "libavutil/random_seed.h"
00040 #include "libavutil/parseutils.h"
00041 #include "libavutil/timecode.h"
00042 #include "libavutil/tree.h"
00043 #include "libavutil/lfg.h"
00044 #include "avfilter.h"
00045 #include "drawutils.h"
00046 #include "formats.h"
00047 #include "internal.h"
00048 #include "video.h"
00049
00050 #include <ft2build.h>
00051 #include <freetype/config/ftheader.h>
00052 #include FT_FREETYPE_H
00053 #include FT_GLYPH_H
00054 #if CONFIG_FONTCONFIG
00055 #include <fontconfig/fontconfig.h>
00056 #endif
00057
00058 static const char *const var_names[] = {
00059 "dar",
00060 "hsub", "vsub",
00061 "line_h", "lh",
00062 "main_h", "h", "H",
00063 "main_w", "w", "W",
00064 "max_glyph_a", "ascent",
00065 "max_glyph_d", "descent",
00066 "max_glyph_h",
00067 "max_glyph_w",
00068 "n",
00069 "sar",
00070 "t",
00071 "text_h", "th",
00072 "text_w", "tw",
00073 "x",
00074 "y",
00075 NULL
00076 };
00077
00078 static const char *const fun2_names[] = {
00079 "rand"
00080 };
00081
00082 static double drand(void *opaque, double min, double max)
00083 {
00084 return min + (max-min) / UINT_MAX * av_lfg_get(opaque);
00085 }
00086
00087 typedef double (*eval_func2)(void *, double a, double b);
00088
00089 static const eval_func2 fun2[] = {
00090 drand,
00091 NULL
00092 };
00093
00094 enum var_name {
00095 VAR_DAR,
00096 VAR_HSUB, VAR_VSUB,
00097 VAR_LINE_H, VAR_LH,
00098 VAR_MAIN_H, VAR_h, VAR_H,
00099 VAR_MAIN_W, VAR_w, VAR_W,
00100 VAR_MAX_GLYPH_A, VAR_ASCENT,
00101 VAR_MAX_GLYPH_D, VAR_DESCENT,
00102 VAR_MAX_GLYPH_H,
00103 VAR_MAX_GLYPH_W,
00104 VAR_N,
00105 VAR_SAR,
00106 VAR_T,
00107 VAR_TEXT_H, VAR_TH,
00108 VAR_TEXT_W, VAR_TW,
00109 VAR_X,
00110 VAR_Y,
00111 VAR_VARS_NB
00112 };
00113
00114 enum expansion_mode {
00115 EXP_NONE,
00116 EXP_NORMAL,
00117 EXP_STRFTIME,
00118 };
00119
00120 typedef struct {
00121 const AVClass *class;
00122 enum expansion_mode exp_mode;
00123 int reinit;
00124 uint8_t *fontfile;
00125 uint8_t *text;
00126 AVBPrint expanded_text;
00127 int ft_load_flags;
00128 FT_Vector *positions;
00129 size_t nb_positions;
00130 char *textfile;
00131 int x;
00132 int y;
00133 int max_glyph_w;
00134 int max_glyph_h;
00135 int shadowx, shadowy;
00136 unsigned int fontsize;
00137 char *fontcolor_string;
00138 char *boxcolor_string;
00139 char *shadowcolor_string;
00140
00141 short int draw_box;
00142 int use_kerning;
00143 int tabsize;
00144 int fix_bounds;
00145
00146 FFDrawContext dc;
00147 FFDrawColor fontcolor;
00148 FFDrawColor shadowcolor;
00149 FFDrawColor boxcolor;
00150
00151 FT_Library library;
00152 FT_Face face;
00153 struct AVTreeNode *glyphs;
00154 char *x_expr;
00155 char *y_expr;
00156 AVExpr *x_pexpr, *y_pexpr;
00157 int64_t basetime;
00158 double var_values[VAR_VARS_NB];
00159 char *draw_expr;
00160 AVExpr *draw_pexpr;
00161 int draw;
00162 AVLFG prng;
00163 char *tc_opt_string;
00164 AVRational tc_rate;
00165 AVTimecode tc;
00166 int tc24hmax;
00167 int frame_id;
00168 int reload;
00169 } DrawTextContext;
00170
00171 #define OFFSET(x) offsetof(DrawTextContext, x)
00172 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
00173
00174 static const AVOption drawtext_options[]= {
00175 {"fontfile", "set font file", OFFSET(fontfile), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS},
00176 {"text", "set text", OFFSET(text), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS},
00177 {"textfile", "set text file", OFFSET(textfile), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS},
00178 {"fontcolor", "set foreground color", OFFSET(fontcolor_string), AV_OPT_TYPE_STRING, {.str="black"}, CHAR_MIN, CHAR_MAX, FLAGS},
00179 {"boxcolor", "set box color", OFFSET(boxcolor_string), AV_OPT_TYPE_STRING, {.str="white"}, CHAR_MIN, CHAR_MAX, FLAGS},
00180 {"shadowcolor", "set shadow color", OFFSET(shadowcolor_string), AV_OPT_TYPE_STRING, {.str="black"}, CHAR_MIN, CHAR_MAX, FLAGS},
00181 {"box", "set box", OFFSET(draw_box), AV_OPT_TYPE_INT, {.i64=0}, 0, 1 , FLAGS},
00182 {"fontsize", "set font size", OFFSET(fontsize), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX , FLAGS},
00183 {"x", "set x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str="0"}, CHAR_MIN, CHAR_MAX, FLAGS},
00184 {"y", "set y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str="0"}, CHAR_MIN, CHAR_MAX, FLAGS},
00185 {"shadowx", "set x", OFFSET(shadowx), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX , FLAGS},
00186 {"shadowy", "set y", OFFSET(shadowy), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX , FLAGS},
00187 {"tabsize", "set tab size", OFFSET(tabsize), AV_OPT_TYPE_INT, {.i64=4}, 0, INT_MAX , FLAGS},
00188 {"basetime", "set base time", OFFSET(basetime), AV_OPT_TYPE_INT64, {.i64=AV_NOPTS_VALUE}, INT64_MIN, INT64_MAX , FLAGS},
00189 {"draw", "if false do not draw", OFFSET(draw_expr), AV_OPT_TYPE_STRING, {.str="1"}, CHAR_MIN, CHAR_MAX, FLAGS},
00190
00191 {"expansion","set the expansion mode", OFFSET(exp_mode), AV_OPT_TYPE_INT, {.i64=EXP_STRFTIME}, 0, 2, FLAGS, "expansion"},
00192 {"none", "set no expansion", OFFSET(exp_mode), AV_OPT_TYPE_CONST, {.i64=EXP_NONE}, 0, 0, FLAGS, "expansion"},
00193 {"normal", "set normal expansion", OFFSET(exp_mode), AV_OPT_TYPE_CONST, {.i64=EXP_NORMAL}, 0, 0, FLAGS, "expansion"},
00194 {"strftime", "set strftime expansion (deprecated)", OFFSET(exp_mode), AV_OPT_TYPE_CONST, {.i64=EXP_STRFTIME}, 0, 0, FLAGS, "expansion"},
00195
00196 {"timecode", "set initial timecode", OFFSET(tc_opt_string), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS},
00197 {"tc24hmax", "set 24 hours max (timecode only)", OFFSET(tc24hmax), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS},
00198 {"timecode_rate", "set rate (timecode only)", OFFSET(tc_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, FLAGS},
00199 {"r", "set rate (timecode only)", OFFSET(tc_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, FLAGS},
00200 {"rate", "set rate (timecode only)", OFFSET(tc_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, FLAGS},
00201 {"reload", "reload text file for each frame", OFFSET(reload), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS},
00202 {"fix_bounds", "if true, check and fix text coords to avoid clipping", OFFSET(fix_bounds), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS},
00203
00204
00205 {"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"},
00206 {"default", "set default", 0, AV_OPT_TYPE_CONST, {.i64=FT_LOAD_DEFAULT}, INT_MIN, INT_MAX, FLAGS, "ft_load_flags"},
00207 {"no_scale", "set no_scale", 0, AV_OPT_TYPE_CONST, {.i64=FT_LOAD_NO_SCALE}, INT_MIN, INT_MAX, FLAGS, "ft_load_flags"},
00208 {"no_hinting", "set no_hinting", 0, AV_OPT_TYPE_CONST, {.i64=FT_LOAD_NO_HINTING}, INT_MIN, INT_MAX, FLAGS, "ft_load_flags"},
00209 {"render", "set render", 0, AV_OPT_TYPE_CONST, {.i64=FT_LOAD_RENDER}, INT_MIN, INT_MAX, FLAGS, "ft_load_flags"},
00210 {"no_bitmap", "set no_bitmap", 0, AV_OPT_TYPE_CONST, {.i64=FT_LOAD_NO_BITMAP}, INT_MIN, INT_MAX, FLAGS, "ft_load_flags"},
00211 {"vertical_layout", "set vertical_layout", 0, AV_OPT_TYPE_CONST, {.i64=FT_LOAD_VERTICAL_LAYOUT}, INT_MIN, INT_MAX, FLAGS, "ft_load_flags"},
00212 {"force_autohint", "set force_autohint", 0, AV_OPT_TYPE_CONST, {.i64=FT_LOAD_FORCE_AUTOHINT}, INT_MIN, INT_MAX, FLAGS, "ft_load_flags"},
00213 {"crop_bitmap", "set crop_bitmap", 0, AV_OPT_TYPE_CONST, {.i64=FT_LOAD_CROP_BITMAP}, INT_MIN, INT_MAX, FLAGS, "ft_load_flags"},
00214 {"pedantic", "set pedantic", 0, AV_OPT_TYPE_CONST, {.i64=FT_LOAD_PEDANTIC}, INT_MIN, INT_MAX, FLAGS, "ft_load_flags"},
00215 {"ignore_global_advance_width", "set ignore_global_advance_width", 0, AV_OPT_TYPE_CONST, {.i64=FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH}, INT_MIN, INT_MAX, FLAGS, "ft_load_flags"},
00216 {"no_recurse", "set no_recurse", 0, AV_OPT_TYPE_CONST, {.i64=FT_LOAD_NO_RECURSE}, INT_MIN, INT_MAX, FLAGS, "ft_load_flags"},
00217 {"ignore_transform", "set ignore_transform", 0, AV_OPT_TYPE_CONST, {.i64=FT_LOAD_IGNORE_TRANSFORM}, INT_MIN, INT_MAX, FLAGS, "ft_load_flags"},
00218 {"monochrome", "set monochrome", 0, AV_OPT_TYPE_CONST, {.i64=FT_LOAD_MONOCHROME}, INT_MIN, INT_MAX, FLAGS, "ft_load_flags"},
00219 {"linear_design", "set linear_design", 0, AV_OPT_TYPE_CONST, {.i64=FT_LOAD_LINEAR_DESIGN}, INT_MIN, INT_MAX, FLAGS, "ft_load_flags"},
00220 {"no_autohint", "set no_autohint", 0, AV_OPT_TYPE_CONST, {.i64=FT_LOAD_NO_AUTOHINT}, INT_MIN, INT_MAX, FLAGS, "ft_load_flags"},
00221 {NULL},
00222 };
00223
00224 AVFILTER_DEFINE_CLASS(drawtext);
00225
00226 #undef __FTERRORS_H__
00227 #define FT_ERROR_START_LIST {
00228 #define FT_ERRORDEF(e, v, s) { (e), (s) },
00229 #define FT_ERROR_END_LIST { 0, NULL } };
00230
00231 struct ft_error
00232 {
00233 int err;
00234 const char *err_msg;
00235 } static ft_errors[] =
00236 #include FT_ERRORS_H
00237
00238 #define FT_ERRMSG(e) ft_errors[e].err_msg
00239
00240 typedef struct {
00241 FT_Glyph *glyph;
00242 uint32_t code;
00243 FT_Bitmap bitmap;
00244 FT_BBox bbox;
00245 int advance;
00246 int bitmap_left;
00247 int bitmap_top;
00248 } Glyph;
00249
00250 static int glyph_cmp(void *key, const void *b)
00251 {
00252 const Glyph *a = key, *bb = b;
00253 int64_t diff = (int64_t)a->code - (int64_t)bb->code;
00254 return diff > 0 ? 1 : diff < 0 ? -1 : 0;
00255 }
00256
00260 static int load_glyph(AVFilterContext *ctx, Glyph **glyph_ptr, uint32_t code)
00261 {
00262 DrawTextContext *dtext = ctx->priv;
00263 Glyph *glyph;
00264 struct AVTreeNode *node = NULL;
00265 int ret;
00266
00267
00268 if (FT_Load_Char(dtext->face, code, dtext->ft_load_flags))
00269 return AVERROR(EINVAL);
00270
00271
00272 if (!(glyph = av_mallocz(sizeof(*glyph))) ||
00273 !(glyph->glyph = av_mallocz(sizeof(*glyph->glyph)))) {
00274 ret = AVERROR(ENOMEM);
00275 goto error;
00276 }
00277 glyph->code = code;
00278
00279 if (FT_Get_Glyph(dtext->face->glyph, glyph->glyph)) {
00280 ret = AVERROR(EINVAL);
00281 goto error;
00282 }
00283
00284 glyph->bitmap = dtext->face->glyph->bitmap;
00285 glyph->bitmap_left = dtext->face->glyph->bitmap_left;
00286 glyph->bitmap_top = dtext->face->glyph->bitmap_top;
00287 glyph->advance = dtext->face->glyph->advance.x >> 6;
00288
00289
00290 FT_Glyph_Get_CBox(*glyph->glyph, ft_glyph_bbox_pixels, &glyph->bbox);
00291
00292
00293 if (!(node = av_tree_node_alloc())) {
00294 ret = AVERROR(ENOMEM);
00295 goto error;
00296 }
00297 av_tree_insert(&dtext->glyphs, glyph, glyph_cmp, &node);
00298
00299 if (glyph_ptr)
00300 *glyph_ptr = glyph;
00301 return 0;
00302
00303 error:
00304 if (glyph)
00305 av_freep(&glyph->glyph);
00306 av_freep(&glyph);
00307 av_freep(&node);
00308 return ret;
00309 }
00310
00311 static int load_font_file(AVFilterContext *ctx, const char *path, int index,
00312 const char **error)
00313 {
00314 DrawTextContext *dtext = ctx->priv;
00315 int err;
00316
00317 err = FT_New_Face(dtext->library, path, index, &dtext->face);
00318 if (err) {
00319 *error = FT_ERRMSG(err);
00320 return AVERROR(EINVAL);
00321 }
00322 return 0;
00323 }
00324
00325 #if CONFIG_FONTCONFIG
00326 static int load_font_fontconfig(AVFilterContext *ctx, const char **error)
00327 {
00328 DrawTextContext *dtext = ctx->priv;
00329 FcConfig *fontconfig;
00330 FcPattern *pattern, *fpat;
00331 FcResult result = FcResultMatch;
00332 FcChar8 *filename;
00333 int err, index;
00334 double size;
00335
00336 fontconfig = FcInitLoadConfigAndFonts();
00337 if (!fontconfig) {
00338 *error = "impossible to init fontconfig\n";
00339 return AVERROR(EINVAL);
00340 }
00341 pattern = FcNameParse(dtext->fontfile ? dtext->fontfile :
00342 (uint8_t *)(intptr_t)"default");
00343 if (!pattern) {
00344 *error = "could not parse fontconfig pattern";
00345 return AVERROR(EINVAL);
00346 }
00347 if (!FcConfigSubstitute(fontconfig, pattern, FcMatchPattern)) {
00348 *error = "could not substitue fontconfig options";
00349 return AVERROR(EINVAL);
00350 }
00351 FcDefaultSubstitute(pattern);
00352 fpat = FcFontMatch(fontconfig, pattern, &result);
00353 if (!fpat || result != FcResultMatch) {
00354 *error = "impossible to find a matching font";
00355 return AVERROR(EINVAL);
00356 }
00357 if (FcPatternGetString (fpat, FC_FILE, 0, &filename) != FcResultMatch ||
00358 FcPatternGetInteger(fpat, FC_INDEX, 0, &index ) != FcResultMatch ||
00359 FcPatternGetDouble (fpat, FC_SIZE, 0, &size ) != FcResultMatch) {
00360 *error = "impossible to find font information";
00361 return AVERROR(EINVAL);
00362 }
00363 av_log(ctx, AV_LOG_INFO, "Using \"%s\"\n", filename);
00364 if (!dtext->fontsize)
00365 dtext->fontsize = size + 0.5;
00366 err = load_font_file(ctx, filename, index, error);
00367 if (err)
00368 return err;
00369 FcPatternDestroy(fpat);
00370 FcPatternDestroy(pattern);
00371 FcConfigDestroy(fontconfig);
00372 return 0;
00373 }
00374 #endif
00375
00376 static int load_font(AVFilterContext *ctx)
00377 {
00378 DrawTextContext *dtext = ctx->priv;
00379 int err;
00380 const char *error = "unknown error\n";
00381
00382
00383 err = load_font_file(ctx, dtext->fontfile, 0, &error);
00384 if (!err)
00385 return 0;
00386 #if CONFIG_FONTCONFIG
00387 err = load_font_fontconfig(ctx, &error);
00388 if (!err)
00389 return 0;
00390 #endif
00391 av_log(ctx, AV_LOG_ERROR, "Could not load font \"%s\": %s\n",
00392 dtext->fontfile, error);
00393 return err;
00394 }
00395
00396 static int load_textfile(AVFilterContext *ctx)
00397 {
00398 DrawTextContext *dtext = ctx->priv;
00399 int err;
00400 uint8_t *textbuf;
00401 size_t textbuf_size;
00402
00403 if ((err = av_file_map(dtext->textfile, &textbuf, &textbuf_size, 0, ctx)) < 0) {
00404 av_log(ctx, AV_LOG_ERROR,
00405 "The text file '%s' could not be read or is empty\n",
00406 dtext->textfile);
00407 return err;
00408 }
00409
00410 if (!(dtext->text = av_realloc(dtext->text, textbuf_size + 1)))
00411 return AVERROR(ENOMEM);
00412 memcpy(dtext->text, textbuf, textbuf_size);
00413 dtext->text[textbuf_size] = 0;
00414 av_file_unmap(textbuf, textbuf_size);
00415
00416 return 0;
00417 }
00418
00419 static av_cold int init(AVFilterContext *ctx, const char *args)
00420 {
00421 int err;
00422 DrawTextContext *dtext = ctx->priv;
00423 Glyph *glyph;
00424
00425 dtext->class = &drawtext_class;
00426 av_opt_set_defaults(dtext);
00427
00428 if ((err = av_set_options_string(dtext, args, "=", ":")) < 0)
00429 return err;
00430
00431 if (!dtext->fontfile && !CONFIG_FONTCONFIG) {
00432 av_log(ctx, AV_LOG_ERROR, "No font filename provided\n");
00433 return AVERROR(EINVAL);
00434 }
00435
00436 if (dtext->textfile) {
00437 if (dtext->text) {
00438 av_log(ctx, AV_LOG_ERROR,
00439 "Both text and text file provided. Please provide only one\n");
00440 return AVERROR(EINVAL);
00441 }
00442 if ((err = load_textfile(ctx)) < 0)
00443 return err;
00444 }
00445
00446 if (dtext->reload && !dtext->textfile)
00447 av_log(ctx, AV_LOG_WARNING, "No file to reload\n");
00448
00449 if (dtext->tc_opt_string) {
00450 int ret = av_timecode_init_from_string(&dtext->tc, dtext->tc_rate,
00451 dtext->tc_opt_string, ctx);
00452 if (ret < 0)
00453 return ret;
00454 if (dtext->tc24hmax)
00455 dtext->tc.flags |= AV_TIMECODE_FLAG_24HOURSMAX;
00456 if (!dtext->text)
00457 dtext->text = av_strdup("");
00458 }
00459
00460 if (!dtext->text) {
00461 av_log(ctx, AV_LOG_ERROR,
00462 "Either text, a valid file or a timecode must be provided\n");
00463 return AVERROR(EINVAL);
00464 }
00465
00466 if ((err = av_parse_color(dtext->fontcolor.rgba, dtext->fontcolor_string, -1, ctx))) {
00467 av_log(ctx, AV_LOG_ERROR,
00468 "Invalid font color '%s'\n", dtext->fontcolor_string);
00469 return err;
00470 }
00471
00472 if ((err = av_parse_color(dtext->boxcolor.rgba, dtext->boxcolor_string, -1, ctx))) {
00473 av_log(ctx, AV_LOG_ERROR,
00474 "Invalid box color '%s'\n", dtext->boxcolor_string);
00475 return err;
00476 }
00477
00478 if ((err = av_parse_color(dtext->shadowcolor.rgba, dtext->shadowcolor_string, -1, ctx))) {
00479 av_log(ctx, AV_LOG_ERROR,
00480 "Invalid shadow color '%s'\n", dtext->shadowcolor_string);
00481 return err;
00482 }
00483
00484 if ((err = FT_Init_FreeType(&(dtext->library)))) {
00485 av_log(ctx, AV_LOG_ERROR,
00486 "Could not load FreeType: %s\n", FT_ERRMSG(err));
00487 return AVERROR(EINVAL);
00488 }
00489
00490 err = load_font(ctx);
00491 if (err)
00492 return err;
00493 if (!dtext->fontsize)
00494 dtext->fontsize = 16;
00495 if ((err = FT_Set_Pixel_Sizes(dtext->face, 0, dtext->fontsize))) {
00496 av_log(ctx, AV_LOG_ERROR, "Could not set font size to %d pixels: %s\n",
00497 dtext->fontsize, FT_ERRMSG(err));
00498 return AVERROR(EINVAL);
00499 }
00500
00501 dtext->use_kerning = FT_HAS_KERNING(dtext->face);
00502
00503
00504 load_glyph(ctx, NULL, 0);
00505
00506
00507 if ((err = load_glyph(ctx, &glyph, ' ')) < 0) {
00508 av_log(ctx, AV_LOG_ERROR, "Could not set tabsize.\n");
00509 return err;
00510 }
00511 dtext->tabsize *= glyph->advance;
00512
00513 if (dtext->exp_mode == EXP_STRFTIME &&
00514 (strchr(dtext->text, '%') || strchr(dtext->text, '\\')))
00515 av_log(ctx, AV_LOG_WARNING, "expansion=strftime is deprecated.\n");
00516
00517 av_bprint_init(&dtext->expanded_text, 0, AV_BPRINT_SIZE_UNLIMITED);
00518
00519 return 0;
00520 }
00521
00522 static int query_formats(AVFilterContext *ctx)
00523 {
00524 ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0));
00525 return 0;
00526 }
00527
00528 static int glyph_enu_free(void *opaque, void *elem)
00529 {
00530 Glyph *glyph = elem;
00531
00532 FT_Done_Glyph(*glyph->glyph);
00533 av_freep(&glyph->glyph);
00534 av_free(elem);
00535 return 0;
00536 }
00537
00538 static av_cold void uninit(AVFilterContext *ctx)
00539 {
00540 DrawTextContext *dtext = ctx->priv;
00541
00542 av_expr_free(dtext->x_pexpr); dtext->x_pexpr = NULL;
00543 av_expr_free(dtext->y_pexpr); dtext->y_pexpr = NULL;
00544 av_expr_free(dtext->draw_pexpr); dtext->draw_pexpr = NULL;
00545 av_opt_free(dtext);
00546
00547 av_freep(&dtext->positions);
00548 dtext->nb_positions = 0;
00549
00550 av_tree_enumerate(dtext->glyphs, NULL, NULL, glyph_enu_free);
00551 av_tree_destroy(dtext->glyphs);
00552 dtext->glyphs = NULL;
00553
00554 FT_Done_Face(dtext->face);
00555 FT_Done_FreeType(dtext->library);
00556
00557 av_bprint_finalize(&dtext->expanded_text, NULL);
00558 }
00559
00560 static inline int is_newline(uint32_t c)
00561 {
00562 return c == '\n' || c == '\r' || c == '\f' || c == '\v';
00563 }
00564
00565 static int config_input(AVFilterLink *inlink)
00566 {
00567 AVFilterContext *ctx = inlink->dst;
00568 DrawTextContext *dtext = ctx->priv;
00569 int ret;
00570
00571 ff_draw_init(&dtext->dc, inlink->format, 0);
00572 ff_draw_color(&dtext->dc, &dtext->fontcolor, dtext->fontcolor.rgba);
00573 ff_draw_color(&dtext->dc, &dtext->shadowcolor, dtext->shadowcolor.rgba);
00574 ff_draw_color(&dtext->dc, &dtext->boxcolor, dtext->boxcolor.rgba);
00575
00576 dtext->var_values[VAR_w] = dtext->var_values[VAR_W] = dtext->var_values[VAR_MAIN_W] = inlink->w;
00577 dtext->var_values[VAR_h] = dtext->var_values[VAR_H] = dtext->var_values[VAR_MAIN_H] = inlink->h;
00578 dtext->var_values[VAR_SAR] = inlink->sample_aspect_ratio.num ? av_q2d(inlink->sample_aspect_ratio) : 1;
00579 dtext->var_values[VAR_DAR] = (double)inlink->w / inlink->h * dtext->var_values[VAR_SAR];
00580 dtext->var_values[VAR_HSUB] = 1 << dtext->dc.hsub_max;
00581 dtext->var_values[VAR_VSUB] = 1 << dtext->dc.vsub_max;
00582 dtext->var_values[VAR_X] = NAN;
00583 dtext->var_values[VAR_Y] = NAN;
00584 if (!dtext->reinit)
00585 dtext->var_values[VAR_N] = 0;
00586 dtext->var_values[VAR_T] = NAN;
00587
00588 av_lfg_init(&dtext->prng, av_get_random_seed());
00589
00590 if ((ret = av_expr_parse(&dtext->x_pexpr, dtext->x_expr, var_names,
00591 NULL, NULL, fun2_names, fun2, 0, ctx)) < 0 ||
00592 (ret = av_expr_parse(&dtext->y_pexpr, dtext->y_expr, var_names,
00593 NULL, NULL, fun2_names, fun2, 0, ctx)) < 0 ||
00594 (ret = av_expr_parse(&dtext->draw_pexpr, dtext->draw_expr, var_names,
00595 NULL, NULL, fun2_names, fun2, 0, ctx)) < 0)
00596
00597 return AVERROR(EINVAL);
00598
00599 return 0;
00600 }
00601
00602 static int command(AVFilterContext *ctx, const char *cmd, const char *arg, char *res, int res_len, int flags)
00603 {
00604 DrawTextContext *dtext = ctx->priv;
00605
00606 if (!strcmp(cmd, "reinit")) {
00607 int ret;
00608 uninit(ctx);
00609 dtext->reinit = 1;
00610 if ((ret = init(ctx, arg)) < 0)
00611 return ret;
00612 return config_input(ctx->inputs[0]);
00613 }
00614
00615 return AVERROR(ENOSYS);
00616 }
00617
00618 static int func_pts(AVFilterContext *ctx, AVBPrint *bp,
00619 char *fct, unsigned argc, char **argv, int tag)
00620 {
00621 DrawTextContext *dtext = ctx->priv;
00622
00623 av_bprintf(bp, "%.6f", dtext->var_values[VAR_T]);
00624 return 0;
00625 }
00626
00627 static int func_frame_num(AVFilterContext *ctx, AVBPrint *bp,
00628 char *fct, unsigned argc, char **argv, int tag)
00629 {
00630 DrawTextContext *dtext = ctx->priv;
00631
00632 av_bprintf(bp, "%d", (int)dtext->var_values[VAR_N]);
00633 return 0;
00634 }
00635
00636 #if !HAVE_LOCALTIME_R
00637 static void localtime_r(const time_t *t, struct tm *tm)
00638 {
00639 *tm = *localtime(t);
00640 }
00641 #endif
00642
00643 static int func_strftime(AVFilterContext *ctx, AVBPrint *bp,
00644 char *fct, unsigned argc, char **argv, int tag)
00645 {
00646 const char *fmt = argc ? argv[0] : "%Y-%m-%d %H:%M:%S";
00647 time_t now;
00648 struct tm tm;
00649
00650 time(&now);
00651 if (tag == 'L')
00652 localtime_r(&now, &tm);
00653 else
00654 tm = *gmtime(&now);
00655 av_bprint_strftime(bp, fmt, &tm);
00656 return 0;
00657 }
00658
00659 static int func_eval_expr(AVFilterContext *ctx, AVBPrint *bp,
00660 char *fct, unsigned argc, char **argv, int tag)
00661 {
00662 DrawTextContext *dtext = ctx->priv;
00663 double res;
00664 int ret;
00665
00666 ret = av_expr_parse_and_eval(&res, argv[0], var_names, dtext->var_values,
00667 NULL, NULL, fun2_names, fun2,
00668 &dtext->prng, 0, ctx);
00669 if (ret < 0)
00670 av_log(ctx, AV_LOG_ERROR,
00671 "Expression '%s' for the expr text expansion function is not valid\n",
00672 argv[0]);
00673 else
00674 av_bprintf(bp, "%f", res);
00675
00676 return ret;
00677 }
00678
00679 static const struct drawtext_function {
00680 const char *name;
00681 unsigned argc_min, argc_max;
00682 int tag;
00683 int (*func)(AVFilterContext *, AVBPrint *, char *, unsigned, char **, int);
00684 } functions[] = {
00685 { "expr", 1, 1, 0, func_eval_expr },
00686 { "e", 1, 1, 0, func_eval_expr },
00687 { "pts", 0, 0, 0, func_pts },
00688 { "gmtime", 0, 1, 'G', func_strftime },
00689 { "localtime", 0, 1, 'L', func_strftime },
00690 { "frame_num", 0, 0, 0, func_frame_num },
00691 { "n", 0, 0, 0, func_frame_num },
00692 };
00693
00694 static int eval_function(AVFilterContext *ctx, AVBPrint *bp, char *fct,
00695 unsigned argc, char **argv)
00696 {
00697 unsigned i;
00698
00699 for (i = 0; i < FF_ARRAY_ELEMS(functions); i++) {
00700 if (strcmp(fct, functions[i].name))
00701 continue;
00702 if (argc < functions[i].argc_min) {
00703 av_log(ctx, AV_LOG_ERROR, "%%{%s} requires at least %d arguments\n",
00704 fct, functions[i].argc_min);
00705 return AVERROR(EINVAL);
00706 }
00707 if (argc > functions[i].argc_max) {
00708 av_log(ctx, AV_LOG_ERROR, "%%{%s} requires at most %d arguments\n",
00709 fct, functions[i].argc_max);
00710 return AVERROR(EINVAL);
00711 }
00712 break;
00713 }
00714 if (i >= FF_ARRAY_ELEMS(functions)) {
00715 av_log(ctx, AV_LOG_ERROR, "%%{%s} is not known\n", fct);
00716 return AVERROR(EINVAL);
00717 }
00718 return functions[i].func(ctx, bp, fct, argc, argv, functions[i].tag);
00719 }
00720
00721 static int expand_function(AVFilterContext *ctx, AVBPrint *bp, char **rtext)
00722 {
00723 const char *text = *rtext;
00724 char *argv[16] = { NULL };
00725 unsigned argc = 0, i;
00726 int ret;
00727
00728 if (*text != '{') {
00729 av_log(ctx, AV_LOG_ERROR, "Stray %% near '%s'\n", text);
00730 return AVERROR(EINVAL);
00731 }
00732 text++;
00733 while (1) {
00734 if (!(argv[argc++] = av_get_token(&text, ":}"))) {
00735 ret = AVERROR(ENOMEM);
00736 goto end;
00737 }
00738 if (!*text) {
00739 av_log(ctx, AV_LOG_ERROR, "Unterminated %%{} near '%s'\n", *rtext);
00740 ret = AVERROR(EINVAL);
00741 goto end;
00742 }
00743 if (argc == FF_ARRAY_ELEMS(argv))
00744 av_freep(&argv[--argc]);
00745 if (*text == '}')
00746 break;
00747 text++;
00748 }
00749
00750 if ((ret = eval_function(ctx, bp, argv[0], argc - 1, argv + 1)) < 0)
00751 goto end;
00752 ret = 0;
00753 *rtext = (char *)text + 1;
00754
00755 end:
00756 for (i = 0; i < argc; i++)
00757 av_freep(&argv[i]);
00758 return ret;
00759 }
00760
00761 static int expand_text(AVFilterContext *ctx)
00762 {
00763 DrawTextContext *dtext = ctx->priv;
00764 char *text = dtext->text;
00765 AVBPrint *bp = &dtext->expanded_text;
00766 int ret;
00767
00768 av_bprint_clear(bp);
00769 while (*text) {
00770 if (*text == '\\' && text[1]) {
00771 av_bprint_chars(bp, text[1], 1);
00772 text += 2;
00773 } else if (*text == '%') {
00774 text++;
00775 if ((ret = expand_function(ctx, bp, &text)) < 0)
00776 return ret;
00777 } else {
00778 av_bprint_chars(bp, *text, 1);
00779 text++;
00780 }
00781 }
00782 if (!av_bprint_is_complete(bp))
00783 return AVERROR(ENOMEM);
00784 return 0;
00785 }
00786
00787 static int draw_glyphs(DrawTextContext *dtext, AVFilterBufferRef *picref,
00788 int width, int height, const uint8_t rgbcolor[4], FFDrawColor *color, int x, int y)
00789 {
00790 char *text = dtext->expanded_text.str;
00791 uint32_t code = 0;
00792 int i, x1, y1;
00793 uint8_t *p;
00794 Glyph *glyph = NULL;
00795
00796 for (i = 0, p = text; *p; i++) {
00797 Glyph dummy = { 0 };
00798 GET_UTF8(code, *p++, continue;);
00799
00800
00801 if (code == '\n' || code == '\r' || code == '\t')
00802 continue;
00803
00804 dummy.code = code;
00805 glyph = av_tree_find(dtext->glyphs, &dummy, (void *)glyph_cmp, NULL);
00806
00807 if (glyph->bitmap.pixel_mode != FT_PIXEL_MODE_MONO &&
00808 glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
00809 return AVERROR(EINVAL);
00810
00811 x1 = dtext->positions[i].x+dtext->x+x;
00812 y1 = dtext->positions[i].y+dtext->y+y;
00813
00814 ff_blend_mask(&dtext->dc, color,
00815 picref->data, picref->linesize, width, height,
00816 glyph->bitmap.buffer, glyph->bitmap.pitch,
00817 glyph->bitmap.width, glyph->bitmap.rows,
00818 glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO ? 0 : 3,
00819 0, x1, y1);
00820 }
00821
00822 return 0;
00823 }
00824
00825 static int draw_text(AVFilterContext *ctx, AVFilterBufferRef *picref,
00826 int width, int height)
00827 {
00828 DrawTextContext *dtext = ctx->priv;
00829 uint32_t code = 0, prev_code = 0;
00830 int x = 0, y = 0, i = 0, ret;
00831 int max_text_line_w = 0, len;
00832 int box_w, box_h;
00833 char *text = dtext->text;
00834 uint8_t *p;
00835 int y_min = 32000, y_max = -32000;
00836 int x_min = 32000, x_max = -32000;
00837 FT_Vector delta;
00838 Glyph *glyph = NULL, *prev_glyph = NULL;
00839 Glyph dummy = { 0 };
00840
00841 time_t now = time(0);
00842 struct tm ltime;
00843 AVBPrint *bp = &dtext->expanded_text;
00844
00845 av_bprint_clear(bp);
00846
00847 if(dtext->basetime != AV_NOPTS_VALUE)
00848 now= picref->pts*av_q2d(ctx->inputs[0]->time_base) + dtext->basetime/1000000;
00849
00850 switch (dtext->exp_mode) {
00851 case EXP_NONE:
00852 av_bprintf(bp, "%s", dtext->text);
00853 break;
00854 case EXP_NORMAL:
00855 if ((ret = expand_text(ctx)) < 0)
00856 return ret;
00857 break;
00858 case EXP_STRFTIME:
00859 localtime_r(&now, <ime);
00860 av_bprint_strftime(bp, dtext->text, <ime);
00861 break;
00862 }
00863
00864 if (dtext->tc_opt_string) {
00865 char tcbuf[AV_TIMECODE_STR_SIZE];
00866 av_timecode_make_string(&dtext->tc, tcbuf, dtext->frame_id++);
00867 av_bprint_clear(bp);
00868 av_bprintf(bp, "%s%s", dtext->text, tcbuf);
00869 }
00870
00871 if (!av_bprint_is_complete(bp))
00872 return AVERROR(ENOMEM);
00873 text = dtext->expanded_text.str;
00874 if ((len = dtext->expanded_text.len) > dtext->nb_positions) {
00875 if (!(dtext->positions =
00876 av_realloc(dtext->positions, len*sizeof(*dtext->positions))))
00877 return AVERROR(ENOMEM);
00878 dtext->nb_positions = len;
00879 }
00880
00881 x = 0;
00882 y = 0;
00883
00884
00885 for (i = 0, p = text; *p; i++) {
00886 GET_UTF8(code, *p++, continue;);
00887
00888
00889 dummy.code = code;
00890 glyph = av_tree_find(dtext->glyphs, &dummy, glyph_cmp, NULL);
00891 if (!glyph) {
00892 load_glyph(ctx, &glyph, code);
00893 }
00894
00895 y_min = FFMIN(glyph->bbox.yMin, y_min);
00896 y_max = FFMAX(glyph->bbox.yMax, y_max);
00897 x_min = FFMIN(glyph->bbox.xMin, x_min);
00898 x_max = FFMAX(glyph->bbox.xMax, x_max);
00899 }
00900 dtext->max_glyph_h = y_max - y_min;
00901 dtext->max_glyph_w = x_max - x_min;
00902
00903
00904 glyph = NULL;
00905 for (i = 0, p = text; *p; i++) {
00906 GET_UTF8(code, *p++, continue;);
00907
00908
00909 if (prev_code == '\r' && code == '\n')
00910 continue;
00911
00912 prev_code = code;
00913 if (is_newline(code)) {
00914 max_text_line_w = FFMAX(max_text_line_w, x);
00915 y += dtext->max_glyph_h;
00916 x = 0;
00917 continue;
00918 }
00919
00920
00921 prev_glyph = glyph;
00922 dummy.code = code;
00923 glyph = av_tree_find(dtext->glyphs, &dummy, glyph_cmp, NULL);
00924
00925
00926 if (dtext->use_kerning && prev_glyph && glyph->code) {
00927 FT_Get_Kerning(dtext->face, prev_glyph->code, glyph->code,
00928 ft_kerning_default, &delta);
00929 x += delta.x >> 6;
00930 }
00931
00932
00933 dtext->positions[i].x = x + glyph->bitmap_left;
00934 dtext->positions[i].y = y - glyph->bitmap_top + y_max;
00935 if (code == '\t') x = (x / dtext->tabsize + 1)*dtext->tabsize;
00936 else x += glyph->advance;
00937 }
00938
00939 max_text_line_w = FFMAX(x, max_text_line_w);
00940
00941 dtext->var_values[VAR_TW] = dtext->var_values[VAR_TEXT_W] = max_text_line_w;
00942 dtext->var_values[VAR_TH] = dtext->var_values[VAR_TEXT_H] = y + dtext->max_glyph_h;
00943
00944 dtext->var_values[VAR_MAX_GLYPH_W] = dtext->max_glyph_w;
00945 dtext->var_values[VAR_MAX_GLYPH_H] = dtext->max_glyph_h;
00946 dtext->var_values[VAR_MAX_GLYPH_A] = dtext->var_values[VAR_ASCENT ] = y_max;
00947 dtext->var_values[VAR_MAX_GLYPH_D] = dtext->var_values[VAR_DESCENT] = y_min;
00948
00949 dtext->var_values[VAR_LINE_H] = dtext->var_values[VAR_LH] = dtext->max_glyph_h;
00950
00951 dtext->x = dtext->var_values[VAR_X] = av_expr_eval(dtext->x_pexpr, dtext->var_values, &dtext->prng);
00952 dtext->y = dtext->var_values[VAR_Y] = av_expr_eval(dtext->y_pexpr, dtext->var_values, &dtext->prng);
00953 dtext->x = dtext->var_values[VAR_X] = av_expr_eval(dtext->x_pexpr, dtext->var_values, &dtext->prng);
00954 dtext->draw = av_expr_eval(dtext->draw_pexpr, dtext->var_values, &dtext->prng);
00955
00956 if(!dtext->draw)
00957 return 0;
00958
00959 box_w = FFMIN(width - 1 , max_text_line_w);
00960 box_h = FFMIN(height - 1, y + dtext->max_glyph_h);
00961
00962
00963 if (dtext->draw_box)
00964 ff_blend_rectangle(&dtext->dc, &dtext->boxcolor,
00965 picref->data, picref->linesize, width, height,
00966 dtext->x, dtext->y, box_w, box_h);
00967
00968 if (dtext->shadowx || dtext->shadowy) {
00969 if ((ret = draw_glyphs(dtext, picref, width, height, dtext->shadowcolor.rgba,
00970 &dtext->shadowcolor, dtext->shadowx, dtext->shadowy)) < 0)
00971 return ret;
00972 }
00973
00974 if ((ret = draw_glyphs(dtext, picref, width, height, dtext->fontcolor.rgba,
00975 &dtext->fontcolor, 0, 0)) < 0)
00976 return ret;
00977
00978 return 0;
00979 }
00980
00981 static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *frame)
00982 {
00983 AVFilterContext *ctx = inlink->dst;
00984 AVFilterLink *outlink = ctx->outputs[0];
00985 DrawTextContext *dtext = ctx->priv;
00986 int ret;
00987
00988 if (dtext->reload)
00989 if ((ret = load_textfile(ctx)) < 0)
00990 return ret;
00991
00992 dtext->var_values[VAR_T] = frame->pts == AV_NOPTS_VALUE ?
00993 NAN : frame->pts * av_q2d(inlink->time_base);
00994
00995 draw_text(ctx, frame, frame->video->w, frame->video->h);
00996
00997 av_log(ctx, AV_LOG_DEBUG, "n:%d t:%f text_w:%d text_h:%d x:%d y:%d\n",
00998 (int)dtext->var_values[VAR_N], dtext->var_values[VAR_T],
00999 (int)dtext->var_values[VAR_TEXT_W], (int)dtext->var_values[VAR_TEXT_H],
01000 dtext->x, dtext->y);
01001
01002 dtext->var_values[VAR_N] += 1.0;
01003
01004 return ff_filter_frame(outlink, frame);
01005 }
01006
01007 static const AVFilterPad avfilter_vf_drawtext_inputs[] = {
01008 {
01009 .name = "default",
01010 .type = AVMEDIA_TYPE_VIDEO,
01011 .get_video_buffer = ff_null_get_video_buffer,
01012 .filter_frame = filter_frame,
01013 .config_props = config_input,
01014 .min_perms = AV_PERM_WRITE |
01015 AV_PERM_READ,
01016 },
01017 { NULL }
01018 };
01019
01020 static const AVFilterPad avfilter_vf_drawtext_outputs[] = {
01021 {
01022 .name = "default",
01023 .type = AVMEDIA_TYPE_VIDEO,
01024 },
01025 { NULL }
01026 };
01027
01028 AVFilter avfilter_vf_drawtext = {
01029 .name = "drawtext",
01030 .description = NULL_IF_CONFIG_SMALL("Draw text on top of video frames using libfreetype library."),
01031 .priv_size = sizeof(DrawTextContext),
01032 .init = init,
01033 .uninit = uninit,
01034 .query_formats = query_formats,
01035
01036 .inputs = avfilter_vf_drawtext_inputs,
01037 .outputs = avfilter_vf_drawtext_outputs,
01038 .process_command = command,
01039 .priv_class = &drawtext_class,
01040 };