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