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/file.h"
00035 #include "libavutil/eval.h"
00036 #include "libavutil/opt.h"
00037 #include "libavutil/random_seed.h"
00038 #include "libavutil/parseutils.h"
00039 #include "libavutil/timecode.h"
00040 #include "libavutil/tree.h"
00041 #include "libavutil/lfg.h"
00042 #include "avfilter.h"
00043 #include "drawutils.h"
00044 #include "video.h"
00045
00046 #undef time
00047
00048 #include <ft2build.h>
00049 #include <freetype/config/ftheader.h>
00050 #include FT_FREETYPE_H
00051 #include FT_GLYPH_H
00052 #if CONFIG_FONTCONFIG
00053 #include <fontconfig/fontconfig.h>
00054 #endif
00055
00056 static const char *const var_names[] = {
00057 "dar",
00058 "hsub", "vsub",
00059 "line_h", "lh",
00060 "main_h", "h", "H",
00061 "main_w", "w", "W",
00062 "max_glyph_a", "ascent",
00063 "max_glyph_d", "descent",
00064 "max_glyph_h",
00065 "max_glyph_w",
00066 "n",
00067 "sar",
00068 "t",
00069 "text_h", "th",
00070 "text_w", "tw",
00071 "x",
00072 "y",
00073 NULL
00074 };
00075
00076 static const char *const fun2_names[] = {
00077 "rand"
00078 };
00079
00080 static double drand(void *opaque, double min, double max)
00081 {
00082 return min + (max-min) / UINT_MAX * av_lfg_get(opaque);
00083 }
00084
00085 typedef double (*eval_func2)(void *, double a, double b);
00086
00087 static const eval_func2 fun2[] = {
00088 drand,
00089 NULL
00090 };
00091
00092 enum var_name {
00093 VAR_DAR,
00094 VAR_HSUB, VAR_VSUB,
00095 VAR_LINE_H, VAR_LH,
00096 VAR_MAIN_H, VAR_h, VAR_H,
00097 VAR_MAIN_W, VAR_w, VAR_W,
00098 VAR_MAX_GLYPH_A, VAR_ASCENT,
00099 VAR_MAX_GLYPH_D, VAR_DESCENT,
00100 VAR_MAX_GLYPH_H,
00101 VAR_MAX_GLYPH_W,
00102 VAR_N,
00103 VAR_SAR,
00104 VAR_T,
00105 VAR_TEXT_H, VAR_TH,
00106 VAR_TEXT_W, VAR_TW,
00107 VAR_X,
00108 VAR_Y,
00109 VAR_VARS_NB
00110 };
00111
00112 typedef struct {
00113 const AVClass *class;
00114 int reinit;
00115 uint8_t *fontfile;
00116 uint8_t *text;
00117 uint8_t *expanded_text;
00118 size_t expanded_text_size;
00119 int ft_load_flags;
00120 FT_Vector *positions;
00121 size_t nb_positions;
00122 char *textfile;
00123 int x;
00124 int y;
00125 int max_glyph_w;
00126 int max_glyph_h;
00127 int shadowx, shadowy;
00128 unsigned int fontsize;
00129 char *fontcolor_string;
00130 char *boxcolor_string;
00131 char *shadowcolor_string;
00132
00133 short int draw_box;
00134 int use_kerning;
00135 int tabsize;
00136 int fix_bounds;
00137
00138 FFDrawContext dc;
00139 FFDrawColor fontcolor;
00140 FFDrawColor shadowcolor;
00141 FFDrawColor boxcolor;
00142
00143 FT_Library library;
00144 FT_Face face;
00145 struct AVTreeNode *glyphs;
00146 char *x_expr;
00147 char *y_expr;
00148 AVExpr *x_pexpr, *y_pexpr;
00149 int64_t basetime;
00150 double var_values[VAR_VARS_NB];
00151 char *draw_expr;
00152 AVExpr *draw_pexpr;
00153 int draw;
00154 AVLFG prng;
00155 char *tc_opt_string;
00156 AVRational tc_rate;
00157 AVTimecode tc;
00158 int tc24hmax;
00159 int frame_id;
00160 } DrawTextContext;
00161
00162 #define OFFSET(x) offsetof(DrawTextContext, x)
00163
00164 static const AVOption drawtext_options[]= {
00165 {"fontfile", "set font file", OFFSET(fontfile), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX },
00166 {"text", "set text", OFFSET(text), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX },
00167 {"textfile", "set text file", OFFSET(textfile), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX },
00168 {"fontcolor", "set foreground color", OFFSET(fontcolor_string), AV_OPT_TYPE_STRING, {.str="black"}, CHAR_MIN, CHAR_MAX },
00169 {"boxcolor", "set box color", OFFSET(boxcolor_string), AV_OPT_TYPE_STRING, {.str="white"}, CHAR_MIN, CHAR_MAX },
00170 {"shadowcolor", "set shadow color", OFFSET(shadowcolor_string), AV_OPT_TYPE_STRING, {.str="black"}, CHAR_MIN, CHAR_MAX },
00171 {"box", "set box", OFFSET(draw_box), AV_OPT_TYPE_INT, {.dbl=0}, 0, 1 },
00172 {"fontsize", "set font size", OFFSET(fontsize), AV_OPT_TYPE_INT, {.dbl=0}, 0, INT_MAX },
00173 {"x", "set x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str="0"}, CHAR_MIN, CHAR_MAX },
00174 {"y", "set y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str="0"}, CHAR_MIN, CHAR_MAX },
00175 {"shadowx", "set x", OFFSET(shadowx), AV_OPT_TYPE_INT, {.dbl=0}, INT_MIN, INT_MAX },
00176 {"shadowy", "set y", OFFSET(shadowy), AV_OPT_TYPE_INT, {.dbl=0}, INT_MIN, INT_MAX },
00177 {"tabsize", "set tab size", OFFSET(tabsize), AV_OPT_TYPE_INT, {.dbl=4}, 0, INT_MAX },
00178 {"basetime", "set base time", OFFSET(basetime), AV_OPT_TYPE_INT64, {.dbl=AV_NOPTS_VALUE}, INT64_MIN, INT64_MAX },
00179 {"draw", "if false do not draw", OFFSET(draw_expr), AV_OPT_TYPE_STRING, {.str="1"}, CHAR_MIN, CHAR_MAX },
00180 {"timecode", "set initial timecode", OFFSET(tc_opt_string), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX },
00181 {"tc24hmax", "set 24 hours max (timecode only)", OFFSET(tc24hmax), AV_OPT_TYPE_INT, {.dbl=0}, 0, 1 },
00182 {"timecode_rate", "set rate (timecode only)", OFFSET(tc_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX },
00183 {"r", "set rate (timecode only)", OFFSET(tc_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX },
00184 {"rate", "set rate (timecode only)", OFFSET(tc_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX },
00185 {"fix_bounds", "if true, check and fix text coords to avoid clipping",
00186 OFFSET(fix_bounds), AV_OPT_TYPE_INT, {.dbl=1}, 0, 1 },
00187
00188
00189 {"ft_load_flags", "set font loading flags for libfreetype", OFFSET(ft_load_flags), AV_OPT_TYPE_FLAGS, {.dbl=FT_LOAD_DEFAULT|FT_LOAD_RENDER}, 0, INT_MAX, 0, "ft_load_flags" },
00190 {"default", "set default", 0, AV_OPT_TYPE_CONST, {.dbl=FT_LOAD_DEFAULT}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00191 {"no_scale", "set no_scale", 0, AV_OPT_TYPE_CONST, {.dbl=FT_LOAD_NO_SCALE}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00192 {"no_hinting", "set no_hinting", 0, AV_OPT_TYPE_CONST, {.dbl=FT_LOAD_NO_HINTING}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00193 {"render", "set render", 0, AV_OPT_TYPE_CONST, {.dbl=FT_LOAD_RENDER}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00194 {"no_bitmap", "set no_bitmap", 0, AV_OPT_TYPE_CONST, {.dbl=FT_LOAD_NO_BITMAP}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00195 {"vertical_layout", "set vertical_layout", 0, AV_OPT_TYPE_CONST, {.dbl=FT_LOAD_VERTICAL_LAYOUT}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00196 {"force_autohint", "set force_autohint", 0, AV_OPT_TYPE_CONST, {.dbl=FT_LOAD_FORCE_AUTOHINT}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00197 {"crop_bitmap", "set crop_bitmap", 0, AV_OPT_TYPE_CONST, {.dbl=FT_LOAD_CROP_BITMAP}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00198 {"pedantic", "set pedantic", 0, AV_OPT_TYPE_CONST, {.dbl=FT_LOAD_PEDANTIC}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00199 {"ignore_global_advance_width", "set ignore_global_advance_width", 0, AV_OPT_TYPE_CONST, {.dbl=FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00200 {"no_recurse", "set no_recurse", 0, AV_OPT_TYPE_CONST, {.dbl=FT_LOAD_NO_RECURSE}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00201 {"ignore_transform", "set ignore_transform", 0, AV_OPT_TYPE_CONST, {.dbl=FT_LOAD_IGNORE_TRANSFORM}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00202 {"monochrome", "set monochrome", 0, AV_OPT_TYPE_CONST, {.dbl=FT_LOAD_MONOCHROME}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00203 {"linear_design", "set linear_design", 0, AV_OPT_TYPE_CONST, {.dbl=FT_LOAD_LINEAR_DESIGN}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00204 {"no_autohint", "set no_autohint", 0, AV_OPT_TYPE_CONST, {.dbl=FT_LOAD_NO_AUTOHINT}, INT_MIN, INT_MAX, 0, "ft_load_flags" },
00205 {NULL},
00206 };
00207
00208 static const char *drawtext_get_name(void *ctx)
00209 {
00210 return "drawtext";
00211 }
00212
00213 static const AVClass drawtext_class = {
00214 "DrawTextContext",
00215 drawtext_get_name,
00216 drawtext_options
00217 };
00218
00219 #undef __FTERRORS_H__
00220 #define FT_ERROR_START_LIST {
00221 #define FT_ERRORDEF(e, v, s) { (e), (s) },
00222 #define FT_ERROR_END_LIST { 0, NULL } };
00223
00224 struct ft_error
00225 {
00226 int err;
00227 const char *err_msg;
00228 } static ft_errors[] =
00229 #include FT_ERRORS_H
00230
00231 #define FT_ERRMSG(e) ft_errors[e].err_msg
00232
00233 typedef struct {
00234 FT_Glyph *glyph;
00235 uint32_t code;
00236 FT_Bitmap bitmap;
00237 FT_BBox bbox;
00238 int advance;
00239 int bitmap_left;
00240 int bitmap_top;
00241 } Glyph;
00242
00243 static int glyph_cmp(void *key, const void *b)
00244 {
00245 const Glyph *a = key, *bb = b;
00246 int64_t diff = (int64_t)a->code - (int64_t)bb->code;
00247 return diff > 0 ? 1 : diff < 0 ? -1 : 0;
00248 }
00249
00253 static int load_glyph(AVFilterContext *ctx, Glyph **glyph_ptr, uint32_t code)
00254 {
00255 DrawTextContext *dtext = ctx->priv;
00256 Glyph *glyph;
00257 struct AVTreeNode *node = NULL;
00258 int ret;
00259
00260
00261 if (FT_Load_Char(dtext->face, code, dtext->ft_load_flags))
00262 return AVERROR(EINVAL);
00263
00264
00265 if (!(glyph = av_mallocz(sizeof(*glyph))) ||
00266 !(glyph->glyph = av_mallocz(sizeof(*glyph->glyph)))) {
00267 ret = AVERROR(ENOMEM);
00268 goto error;
00269 }
00270 glyph->code = code;
00271
00272 if (FT_Get_Glyph(dtext->face->glyph, glyph->glyph)) {
00273 ret = AVERROR(EINVAL);
00274 goto error;
00275 }
00276
00277 glyph->bitmap = dtext->face->glyph->bitmap;
00278 glyph->bitmap_left = dtext->face->glyph->bitmap_left;
00279 glyph->bitmap_top = dtext->face->glyph->bitmap_top;
00280 glyph->advance = dtext->face->glyph->advance.x >> 6;
00281
00282
00283 FT_Glyph_Get_CBox(*glyph->glyph, ft_glyph_bbox_pixels, &glyph->bbox);
00284
00285
00286 if (!(node = av_mallocz(av_tree_node_size))) {
00287 ret = AVERROR(ENOMEM);
00288 goto error;
00289 }
00290 av_tree_insert(&dtext->glyphs, glyph, glyph_cmp, &node);
00291
00292 if (glyph_ptr)
00293 *glyph_ptr = glyph;
00294 return 0;
00295
00296 error:
00297 if (glyph)
00298 av_freep(&glyph->glyph);
00299 av_freep(&glyph);
00300 av_freep(&node);
00301 return ret;
00302 }
00303
00304 static int load_font_file(AVFilterContext *ctx, const char *path, int index,
00305 const char **error)
00306 {
00307 DrawTextContext *dtext = ctx->priv;
00308 int err;
00309
00310 err = FT_New_Face(dtext->library, path, index, &dtext->face);
00311 if (err) {
00312 *error = FT_ERRMSG(err);
00313 return AVERROR(EINVAL);
00314 }
00315 return 0;
00316 }
00317
00318 #if CONFIG_FONTCONFIG
00319 static int load_font_fontconfig(AVFilterContext *ctx, const char **error)
00320 {
00321 DrawTextContext *dtext = ctx->priv;
00322 FcConfig *fontconfig;
00323 FcPattern *pattern, *fpat;
00324 FcResult result = FcResultMatch;
00325 FcChar8 *filename;
00326 int err, index;
00327 double size;
00328
00329 fontconfig = FcInitLoadConfigAndFonts();
00330 if (!fontconfig) {
00331 *error = "impossible to init fontconfig\n";
00332 return AVERROR(EINVAL);
00333 }
00334 pattern = FcNameParse(dtext->fontfile ? dtext->fontfile :
00335 (uint8_t *)(intptr_t)"default");
00336 if (!pattern) {
00337 *error = "could not parse fontconfig pattern";
00338 return AVERROR(EINVAL);
00339 }
00340 if (!FcConfigSubstitute(fontconfig, pattern, FcMatchPattern)) {
00341 *error = "could not substitue fontconfig options";
00342 return AVERROR(EINVAL);
00343 }
00344 FcDefaultSubstitute(pattern);
00345 fpat = FcFontMatch(fontconfig, pattern, &result);
00346 if (!fpat || result != FcResultMatch) {
00347 *error = "impossible to find a matching font";
00348 return AVERROR(EINVAL);
00349 }
00350 if (FcPatternGetString (fpat, FC_FILE, 0, &filename) != FcResultMatch ||
00351 FcPatternGetInteger(fpat, FC_INDEX, 0, &index ) != FcResultMatch ||
00352 FcPatternGetDouble (fpat, FC_SIZE, 0, &size ) != FcResultMatch) {
00353 *error = "impossible to find font information";
00354 return AVERROR(EINVAL);
00355 }
00356 av_log(ctx, AV_LOG_INFO, "Using \"%s\"\n", filename);
00357 if (!dtext->fontsize)
00358 dtext->fontsize = size + 0.5;
00359 err = load_font_file(ctx, filename, index, error);
00360 if (err)
00361 return err;
00362 FcPatternDestroy(fpat);
00363 FcPatternDestroy(pattern);
00364 FcConfigDestroy(fontconfig);
00365 return 0;
00366 }
00367 #endif
00368
00369 static int load_font(AVFilterContext *ctx)
00370 {
00371 DrawTextContext *dtext = ctx->priv;
00372 int err;
00373 const char *error = "unknown error\n";
00374
00375
00376 err = load_font_file(ctx, dtext->fontfile, 0, &error);
00377 if (!err)
00378 return 0;
00379 #if CONFIG_FONTCONFIG
00380 err = load_font_fontconfig(ctx, &error);
00381 if (!err)
00382 return 0;
00383 #endif
00384 av_log(ctx, AV_LOG_ERROR, "Could not load font \"%s\": %s\n",
00385 dtext->fontfile, error);
00386 return err;
00387 }
00388
00389 static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
00390 {
00391 int err;
00392 DrawTextContext *dtext = ctx->priv;
00393 Glyph *glyph;
00394
00395 dtext->class = &drawtext_class;
00396 av_opt_set_defaults(dtext);
00397
00398 if ((err = (av_set_options_string(dtext, args, "=", ":"))) < 0) {
00399 av_log(ctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args);
00400 return err;
00401 }
00402
00403 if (!dtext->fontfile && !CONFIG_FONTCONFIG) {
00404 av_log(ctx, AV_LOG_ERROR, "No font filename provided\n");
00405 return AVERROR(EINVAL);
00406 }
00407
00408 if (dtext->textfile) {
00409 uint8_t *textbuf;
00410 size_t textbuf_size;
00411
00412 if (dtext->text) {
00413 av_log(ctx, AV_LOG_ERROR,
00414 "Both text and text file provided. Please provide only one\n");
00415 return AVERROR(EINVAL);
00416 }
00417 if ((err = av_file_map(dtext->textfile, &textbuf, &textbuf_size, 0, ctx)) < 0) {
00418 av_log(ctx, AV_LOG_ERROR,
00419 "The text file '%s' could not be read or is empty\n",
00420 dtext->textfile);
00421 return err;
00422 }
00423
00424 if (!(dtext->text = av_malloc(textbuf_size+1)))
00425 return AVERROR(ENOMEM);
00426 memcpy(dtext->text, textbuf, textbuf_size);
00427 dtext->text[textbuf_size] = 0;
00428 av_file_unmap(textbuf, textbuf_size);
00429 }
00430
00431 if (dtext->tc_opt_string) {
00432 int ret = av_timecode_init_from_string(&dtext->tc, dtext->tc_rate,
00433 dtext->tc_opt_string, ctx);
00434 if (ret < 0)
00435 return ret;
00436 if (dtext->tc24hmax)
00437 dtext->tc.flags |= AV_TIMECODE_FLAG_24HOURSMAX;
00438 if (!dtext->text)
00439 dtext->text = av_strdup("");
00440 }
00441
00442 if (!dtext->text) {
00443 av_log(ctx, AV_LOG_ERROR,
00444 "Either text, a valid file or a timecode must be provided\n");
00445 return AVERROR(EINVAL);
00446 }
00447
00448 if ((err = av_parse_color(dtext->fontcolor.rgba, dtext->fontcolor_string, -1, ctx))) {
00449 av_log(ctx, AV_LOG_ERROR,
00450 "Invalid font color '%s'\n", dtext->fontcolor_string);
00451 return err;
00452 }
00453
00454 if ((err = av_parse_color(dtext->boxcolor.rgba, dtext->boxcolor_string, -1, ctx))) {
00455 av_log(ctx, AV_LOG_ERROR,
00456 "Invalid box color '%s'\n", dtext->boxcolor_string);
00457 return err;
00458 }
00459
00460 if ((err = av_parse_color(dtext->shadowcolor.rgba, dtext->shadowcolor_string, -1, ctx))) {
00461 av_log(ctx, AV_LOG_ERROR,
00462 "Invalid shadow color '%s'\n", dtext->shadowcolor_string);
00463 return err;
00464 }
00465
00466 if ((err = FT_Init_FreeType(&(dtext->library)))) {
00467 av_log(ctx, AV_LOG_ERROR,
00468 "Could not load FreeType: %s\n", FT_ERRMSG(err));
00469 return AVERROR(EINVAL);
00470 }
00471
00472 err = load_font(ctx);
00473 if (err)
00474 return err;
00475 if (!dtext->fontsize)
00476 dtext->fontsize = 16;
00477 if ((err = FT_Set_Pixel_Sizes(dtext->face, 0, dtext->fontsize))) {
00478 av_log(ctx, AV_LOG_ERROR, "Could not set font size to %d pixels: %s\n",
00479 dtext->fontsize, FT_ERRMSG(err));
00480 return AVERROR(EINVAL);
00481 }
00482
00483 dtext->use_kerning = FT_HAS_KERNING(dtext->face);
00484
00485
00486 load_glyph(ctx, NULL, 0);
00487
00488
00489 if ((err = load_glyph(ctx, &glyph, ' ')) < 0) {
00490 av_log(ctx, AV_LOG_ERROR, "Could not set tabsize.\n");
00491 return err;
00492 }
00493 dtext->tabsize *= glyph->advance;
00494
00495 return 0;
00496 }
00497
00498 static int query_formats(AVFilterContext *ctx)
00499 {
00500 avfilter_set_common_pixel_formats(ctx, ff_draw_supported_pixel_formats(0));
00501 return 0;
00502 }
00503
00504 static int glyph_enu_free(void *opaque, void *elem)
00505 {
00506 Glyph *glyph = elem;
00507
00508 FT_Done_Glyph(*glyph->glyph);
00509 av_freep(&glyph->glyph);
00510 av_free(elem);
00511 return 0;
00512 }
00513
00514 static av_cold void uninit(AVFilterContext *ctx)
00515 {
00516 DrawTextContext *dtext = ctx->priv;
00517
00518 av_expr_free(dtext->x_pexpr); dtext->x_pexpr = NULL;
00519 av_expr_free(dtext->y_pexpr); dtext->y_pexpr = NULL;
00520 av_expr_free(dtext->draw_pexpr); dtext->draw_pexpr = NULL;
00521
00522 av_freep(&dtext->boxcolor_string);
00523 av_freep(&dtext->expanded_text);
00524 av_freep(&dtext->fontcolor_string);
00525 av_freep(&dtext->fontfile);
00526 av_freep(&dtext->shadowcolor_string);
00527 av_freep(&dtext->text);
00528 av_freep(&dtext->x_expr);
00529 av_freep(&dtext->y_expr);
00530 av_freep(&dtext->draw_expr);
00531
00532 av_freep(&dtext->positions);
00533 dtext->nb_positions = 0;
00534
00535 av_tree_enumerate(dtext->glyphs, NULL, NULL, glyph_enu_free);
00536 av_tree_destroy(dtext->glyphs);
00537 dtext->glyphs = NULL;
00538
00539 FT_Done_Face(dtext->face);
00540 FT_Done_FreeType(dtext->library);
00541 }
00542
00543 static inline int is_newline(uint32_t c)
00544 {
00545 return c == '\n' || c == '\r' || c == '\f' || c == '\v';
00546 }
00547
00548 static int config_input(AVFilterLink *inlink)
00549 {
00550 AVFilterContext *ctx = inlink->dst;
00551 DrawTextContext *dtext = ctx->priv;
00552 int ret;
00553
00554 ff_draw_init(&dtext->dc, inlink->format, 0);
00555 ff_draw_color(&dtext->dc, &dtext->fontcolor, dtext->fontcolor.rgba);
00556 ff_draw_color(&dtext->dc, &dtext->shadowcolor, dtext->shadowcolor.rgba);
00557 ff_draw_color(&dtext->dc, &dtext->boxcolor, dtext->boxcolor.rgba);
00558
00559 dtext->var_values[VAR_w] = dtext->var_values[VAR_W] = dtext->var_values[VAR_MAIN_W] = inlink->w;
00560 dtext->var_values[VAR_h] = dtext->var_values[VAR_H] = dtext->var_values[VAR_MAIN_H] = inlink->h;
00561 dtext->var_values[VAR_SAR] = inlink->sample_aspect_ratio.num ? av_q2d(inlink->sample_aspect_ratio) : 1;
00562 dtext->var_values[VAR_DAR] = (double)inlink->w / inlink->h * dtext->var_values[VAR_SAR];
00563 dtext->var_values[VAR_HSUB] = 1 << dtext->dc.hsub_max;
00564 dtext->var_values[VAR_VSUB] = 1 << dtext->dc.vsub_max;
00565 dtext->var_values[VAR_X] = NAN;
00566 dtext->var_values[VAR_Y] = NAN;
00567 if (!dtext->reinit)
00568 dtext->var_values[VAR_N] = 0;
00569 dtext->var_values[VAR_T] = NAN;
00570
00571 av_lfg_init(&dtext->prng, av_get_random_seed());
00572
00573 if ((ret = av_expr_parse(&dtext->x_pexpr, dtext->x_expr, var_names,
00574 NULL, NULL, fun2_names, fun2, 0, ctx)) < 0 ||
00575 (ret = av_expr_parse(&dtext->y_pexpr, dtext->y_expr, var_names,
00576 NULL, NULL, fun2_names, fun2, 0, ctx)) < 0 ||
00577 (ret = av_expr_parse(&dtext->draw_pexpr, dtext->draw_expr, var_names,
00578 NULL, NULL, fun2_names, fun2, 0, ctx)) < 0)
00579
00580 return AVERROR(EINVAL);
00581
00582 return 0;
00583 }
00584
00585 static int command(AVFilterContext *ctx, const char *cmd, const char *arg, char *res, int res_len, int flags)
00586 {
00587 DrawTextContext *dtext = ctx->priv;
00588
00589 if (!strcmp(cmd, "reinit")) {
00590 int ret;
00591 uninit(ctx);
00592 dtext->reinit = 1;
00593 if ((ret = init(ctx, arg, NULL)) < 0)
00594 return ret;
00595 return config_input(ctx->inputs[0]);
00596 }
00597
00598 return AVERROR(ENOSYS);
00599 }
00600
00601 static int draw_glyphs(DrawTextContext *dtext, AVFilterBufferRef *picref,
00602 int width, int height, const uint8_t rgbcolor[4], FFDrawColor *color, int x, int y)
00603 {
00604 char *text = dtext->expanded_text;
00605 uint32_t code = 0;
00606 int i, x1, y1;
00607 uint8_t *p;
00608 Glyph *glyph = NULL;
00609
00610 for (i = 0, p = text; *p; i++) {
00611 Glyph dummy = { 0 };
00612 GET_UTF8(code, *p++, continue;);
00613
00614
00615 if (code == '\n' || code == '\r' || code == '\t')
00616 continue;
00617
00618 dummy.code = code;
00619 glyph = av_tree_find(dtext->glyphs, &dummy, (void *)glyph_cmp, NULL);
00620
00621 if (glyph->bitmap.pixel_mode != FT_PIXEL_MODE_MONO &&
00622 glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
00623 return AVERROR(EINVAL);
00624
00625 x1 = dtext->positions[i].x+dtext->x+x;
00626 y1 = dtext->positions[i].y+dtext->y+y;
00627
00628 ff_blend_mask(&dtext->dc, color,
00629 picref->data, picref->linesize, width, height,
00630 glyph->bitmap.buffer, glyph->bitmap.pitch,
00631 glyph->bitmap.width, glyph->bitmap.rows,
00632 glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO ? 0 : 3,
00633 0, x1, y1);
00634 }
00635
00636 return 0;
00637 }
00638
00639 static int draw_text(AVFilterContext *ctx, AVFilterBufferRef *picref,
00640 int width, int height)
00641 {
00642 DrawTextContext *dtext = ctx->priv;
00643 uint32_t code = 0, prev_code = 0;
00644 int x = 0, y = 0, i = 0, ret;
00645 int max_text_line_w = 0, len;
00646 int box_w, box_h;
00647 char *text = dtext->text;
00648 uint8_t *p;
00649 int y_min = 32000, y_max = -32000;
00650 int x_min = 32000, x_max = -32000;
00651 FT_Vector delta;
00652 Glyph *glyph = NULL, *prev_glyph = NULL;
00653 Glyph dummy = { 0 };
00654
00655 time_t now = time(0);
00656 struct tm ltime;
00657 uint8_t *buf = dtext->expanded_text;
00658 int buf_size = dtext->expanded_text_size;
00659
00660 if(dtext->basetime != AV_NOPTS_VALUE)
00661 now= picref->pts*av_q2d(ctx->inputs[0]->time_base) + dtext->basetime/1000000;
00662
00663 if (!buf) {
00664 buf_size = 2*strlen(dtext->text)+1;
00665 buf = av_malloc(buf_size);
00666 }
00667
00668 #if HAVE_LOCALTIME_R
00669 localtime_r(&now, <ime);
00670 #else
00671 if(strchr(dtext->text, '%'))
00672 ltime= *localtime(&now);
00673 #endif
00674
00675 do {
00676 *buf = 1;
00677 if (strftime(buf, buf_size, dtext->text, <ime) != 0 || *buf == 0)
00678 break;
00679 buf_size *= 2;
00680 } while ((buf = av_realloc(buf, buf_size)));
00681
00682 if (dtext->tc_opt_string) {
00683 char tcbuf[AV_TIMECODE_STR_SIZE];
00684 av_timecode_make_string(&dtext->tc, tcbuf, dtext->frame_id++);
00685 buf = av_asprintf("%s%s", dtext->text, tcbuf);
00686 }
00687
00688 if (!buf)
00689 return AVERROR(ENOMEM);
00690 text = dtext->expanded_text = buf;
00691 dtext->expanded_text_size = buf_size;
00692 if ((len = strlen(text)) > dtext->nb_positions) {
00693 if (!(dtext->positions =
00694 av_realloc(dtext->positions, len*sizeof(*dtext->positions))))
00695 return AVERROR(ENOMEM);
00696 dtext->nb_positions = len;
00697 }
00698
00699 x = 0;
00700 y = 0;
00701
00702
00703 for (i = 0, p = text; *p; i++) {
00704 GET_UTF8(code, *p++, continue;);
00705
00706
00707 dummy.code = code;
00708 glyph = av_tree_find(dtext->glyphs, &dummy, glyph_cmp, NULL);
00709 if (!glyph) {
00710 load_glyph(ctx, &glyph, code);
00711 }
00712
00713 y_min = FFMIN(glyph->bbox.yMin, y_min);
00714 y_max = FFMAX(glyph->bbox.yMax, y_max);
00715 x_min = FFMIN(glyph->bbox.xMin, x_min);
00716 x_max = FFMAX(glyph->bbox.xMax, x_max);
00717 }
00718 dtext->max_glyph_h = y_max - y_min;
00719 dtext->max_glyph_w = x_max - x_min;
00720
00721
00722 glyph = NULL;
00723 for (i = 0, p = text; *p; i++) {
00724 GET_UTF8(code, *p++, continue;);
00725
00726
00727 if (prev_code == '\r' && code == '\n')
00728 continue;
00729
00730 prev_code = code;
00731 if (is_newline(code)) {
00732 max_text_line_w = FFMAX(max_text_line_w, x);
00733 y += dtext->max_glyph_h;
00734 x = 0;
00735 continue;
00736 }
00737
00738
00739 prev_glyph = glyph;
00740 dummy.code = code;
00741 glyph = av_tree_find(dtext->glyphs, &dummy, glyph_cmp, NULL);
00742
00743
00744 if (dtext->use_kerning && prev_glyph && glyph->code) {
00745 FT_Get_Kerning(dtext->face, prev_glyph->code, glyph->code,
00746 ft_kerning_default, &delta);
00747 x += delta.x >> 6;
00748 }
00749
00750
00751 dtext->positions[i].x = x + glyph->bitmap_left;
00752 dtext->positions[i].y = y - glyph->bitmap_top + y_max;
00753 if (code == '\t') x = (x / dtext->tabsize + 1)*dtext->tabsize;
00754 else x += glyph->advance;
00755 }
00756
00757 max_text_line_w = FFMAX(x, max_text_line_w);
00758
00759 dtext->var_values[VAR_TW] = dtext->var_values[VAR_TEXT_W] = max_text_line_w;
00760 dtext->var_values[VAR_TH] = dtext->var_values[VAR_TEXT_H] = y + dtext->max_glyph_h;
00761
00762 dtext->var_values[VAR_MAX_GLYPH_W] = dtext->max_glyph_w;
00763 dtext->var_values[VAR_MAX_GLYPH_H] = dtext->max_glyph_h;
00764 dtext->var_values[VAR_MAX_GLYPH_A] = dtext->var_values[VAR_ASCENT ] = y_max;
00765 dtext->var_values[VAR_MAX_GLYPH_D] = dtext->var_values[VAR_DESCENT] = y_min;
00766
00767 dtext->var_values[VAR_LINE_H] = dtext->var_values[VAR_LH] = dtext->max_glyph_h;
00768
00769 dtext->x = dtext->var_values[VAR_X] = av_expr_eval(dtext->x_pexpr, dtext->var_values, &dtext->prng);
00770 dtext->y = dtext->var_values[VAR_Y] = av_expr_eval(dtext->y_pexpr, dtext->var_values, &dtext->prng);
00771 dtext->x = dtext->var_values[VAR_X] = av_expr_eval(dtext->x_pexpr, dtext->var_values, &dtext->prng);
00772 dtext->draw = av_expr_eval(dtext->draw_pexpr, dtext->var_values, &dtext->prng);
00773
00774 if(!dtext->draw)
00775 return 0;
00776
00777 box_w = FFMIN(width - 1 , max_text_line_w);
00778 box_h = FFMIN(height - 1, y + dtext->max_glyph_h);
00779
00780
00781 if (dtext->draw_box)
00782 ff_blend_rectangle(&dtext->dc, &dtext->boxcolor,
00783 picref->data, picref->linesize, width, height,
00784 dtext->x, dtext->y, box_w, box_h);
00785
00786 if (dtext->shadowx || dtext->shadowy) {
00787 if ((ret = draw_glyphs(dtext, picref, width, height, dtext->shadowcolor.rgba,
00788 &dtext->shadowcolor, dtext->shadowx, dtext->shadowy)) < 0)
00789 return ret;
00790 }
00791
00792 if ((ret = draw_glyphs(dtext, picref, width, height, dtext->fontcolor.rgba,
00793 &dtext->fontcolor, 0, 0)) < 0)
00794 return ret;
00795
00796 return 0;
00797 }
00798
00799 static void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { }
00800
00801 static void end_frame(AVFilterLink *inlink)
00802 {
00803 AVFilterContext *ctx = inlink->dst;
00804 AVFilterLink *outlink = ctx->outputs[0];
00805 DrawTextContext *dtext = ctx->priv;
00806 AVFilterBufferRef *picref = inlink->cur_buf;
00807
00808 dtext->var_values[VAR_T] = picref->pts == AV_NOPTS_VALUE ?
00809 NAN : picref->pts * av_q2d(inlink->time_base);
00810
00811 draw_text(ctx, picref, picref->video->w, picref->video->h);
00812
00813 av_log(ctx, AV_LOG_DEBUG, "n:%d t:%f text_w:%d text_h:%d x:%d y:%d\n",
00814 (int)dtext->var_values[VAR_N], dtext->var_values[VAR_T],
00815 (int)dtext->var_values[VAR_TEXT_W], (int)dtext->var_values[VAR_TEXT_H],
00816 dtext->x, dtext->y);
00817
00818 dtext->var_values[VAR_N] += 1.0;
00819
00820 avfilter_draw_slice(outlink, 0, picref->video->h, 1);
00821 avfilter_end_frame(outlink);
00822 }
00823
00824 AVFilter avfilter_vf_drawtext = {
00825 .name = "drawtext",
00826 .description = NULL_IF_CONFIG_SMALL("Draw text on top of video frames using libfreetype library."),
00827 .priv_size = sizeof(DrawTextContext),
00828 .init = init,
00829 .uninit = uninit,
00830 .query_formats = query_formats,
00831
00832 .inputs = (const AVFilterPad[]) {{ .name = "default",
00833 .type = AVMEDIA_TYPE_VIDEO,
00834 .get_video_buffer = ff_null_get_video_buffer,
00835 .start_frame = ff_null_start_frame,
00836 .draw_slice = null_draw_slice,
00837 .end_frame = end_frame,
00838 .config_props = config_input,
00839 .min_perms = AV_PERM_WRITE |
00840 AV_PERM_READ,
00841 .rej_perms = AV_PERM_PRESERVE },
00842 { .name = NULL}},
00843 .outputs = (const AVFilterPad[]) {{ .name = "default",
00844 .type = AVMEDIA_TYPE_VIDEO, },
00845 { .name = NULL}},
00846 .process_command = command,
00847 };