X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavfilter%2Fvf_drawtext.c;h=7aeea86ce14c4f877ed5615b5a4dd2af48193857;hb=932d8300d38587ebc223e3bacc3c33ed68bf3cf6;hp=87c8d8f8ac63e5e591368211fa8e93d4370be3ab;hpb=f5edfc9e92d1340cd50385081e563da226940e4e;p=ffmpeg diff --git a/libavfilter/vf_drawtext.c b/libavfilter/vf_drawtext.c index 87c8d8f8ac6..7aeea86ce14 100644 --- a/libavfilter/vf_drawtext.c +++ b/libavfilter/vf_drawtext.c @@ -30,6 +30,7 @@ #include #include "libavutil/colorspace.h" +#include "libavutil/common.h" #include "libavutil/file.h" #include "libavutil/eval.h" #include "libavutil/opt.h" @@ -41,6 +42,9 @@ #include "libavutil/lfg.h" #include "avfilter.h" #include "drawutils.h" +#include "formats.h" +#include "internal.h" +#include "video.h" #undef time @@ -49,7 +53,7 @@ #include FT_FREETYPE_H #include FT_GLYPH_H -static const char *var_names[] = { +static const char *const var_names[] = { "E", "PHI", "PI", @@ -64,13 +68,13 @@ static const char *var_names[] = { NULL }; -static const char *fun2_names[] = { - "rand", +static const char *const fun2_names[] = { + "rand" }; static double drand(void *opaque, double min, double max) { - return val = min + (max-min) / UINT_MAX * av_lfg_get(opaque); + return min + (max-min) / UINT_MAX * av_lfg_get(opaque); } typedef double (*eval_func2)(void *, double a, double b); @@ -122,6 +126,7 @@ typedef struct { short int draw_box; ///< draw box around text - true or false int use_kerning; ///< font kerning is used - true/false int tabsize; ///< tab size + int fix_bounds; ///< do we let it go out of frame bounds - t/f FT_Library library; ///< freetype font library handle FT_Face face; ///< freetype font face handle @@ -149,32 +154,34 @@ static const AVOption drawtext_options[]= { {"fontcolor","set foreground color", OFFSET(fontcolor_string), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX }, {"boxcolor", "set box color", OFFSET(boxcolor_string), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX }, {"shadowcolor", "set shadow color", OFFSET(shadowcolor_string), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX }, -{"box", "set box", OFFSET(draw_box), AV_OPT_TYPE_INT, {.dbl=0}, 0, 1 }, -{"fontsize", "set font size", OFFSET(fontsize), AV_OPT_TYPE_INT, {.dbl=16}, 1, 72 }, +{"box", "set box", OFFSET(draw_box), AV_OPT_TYPE_INT, {.i64=0}, 0, 1 }, +{"fontsize", "set font size", OFFSET(fontsize), AV_OPT_TYPE_INT, {.i64=16}, 1, 72 }, {"x", "set x", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str="0"}, CHAR_MIN, CHAR_MAX }, {"y", "set y", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str="0"}, CHAR_MIN, CHAR_MAX }, -{"shadowx", "set x", OFFSET(shadowx), AV_OPT_TYPE_INT, {.dbl=0}, INT_MIN, INT_MAX }, -{"shadowy", "set y", OFFSET(shadowy), AV_OPT_TYPE_INT, {.dbl=0}, INT_MIN, INT_MAX }, -{"tabsize", "set tab size", OFFSET(tabsize), AV_OPT_TYPE_INT, {.dbl=4}, 0, INT_MAX }, +{"shadowx", "set x", OFFSET(shadowx), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX }, +{"shadowy", "set y", OFFSET(shadowy), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX }, +{"tabsize", "set tab size", OFFSET(tabsize), AV_OPT_TYPE_INT, {.i64=4}, 0, INT_MAX }, {"draw", "if false do not draw", OFFSET(d_expr), AV_OPT_TYPE_STRING, {.str="1"}, CHAR_MIN, CHAR_MAX }, +{"fix_bounds", "if true, check and fix text coords to avoid clipping", + OFFSET(fix_bounds), AV_OPT_TYPE_INT, {.i64=1}, 0, 1 }, /* FT_LOAD_* flags */ -{"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" }, -{"default", "set default", 0, AV_OPT_TYPE_CONST, {FT_LOAD_DEFAULT}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, -{"no_scale", "set no_scale", 0, AV_OPT_TYPE_CONST, {FT_LOAD_NO_SCALE}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, -{"no_hinting", "set no_hinting", 0, AV_OPT_TYPE_CONST, {FT_LOAD_NO_HINTING}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, -{"render", "set render", 0, AV_OPT_TYPE_CONST, {FT_LOAD_RENDER}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, -{"no_bitmap", "set no_bitmap", 0, AV_OPT_TYPE_CONST, {FT_LOAD_NO_BITMAP}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, -{"vertical_layout", "set vertical_layout", 0, AV_OPT_TYPE_CONST, {FT_LOAD_VERTICAL_LAYOUT}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, -{"force_autohint", "set force_autohint", 0, AV_OPT_TYPE_CONST, {FT_LOAD_FORCE_AUTOHINT}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, -{"crop_bitmap", "set crop_bitmap", 0, AV_OPT_TYPE_CONST, {FT_LOAD_CROP_BITMAP}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, -{"pedantic", "set pedantic", 0, AV_OPT_TYPE_CONST, {FT_LOAD_PEDANTIC}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, -{"ignore_global_advance_width", "set ignore_global_advance_width", 0, AV_OPT_TYPE_CONST, {FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, -{"no_recurse", "set no_recurse", 0, AV_OPT_TYPE_CONST, {FT_LOAD_NO_RECURSE}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, -{"ignore_transform", "set ignore_transform", 0, AV_OPT_TYPE_CONST, {FT_LOAD_IGNORE_TRANSFORM}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, -{"monochrome", "set monochrome", 0, AV_OPT_TYPE_CONST, {FT_LOAD_MONOCHROME}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, -{"linear_design", "set linear_design", 0, AV_OPT_TYPE_CONST, {FT_LOAD_LINEAR_DESIGN}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, -{"no_autohint", "set no_autohint", 0, AV_OPT_TYPE_CONST, {FT_LOAD_NO_AUTOHINT}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, +{"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, 0, "ft_load_flags" }, +{"default", "set default", 0, AV_OPT_TYPE_CONST, {.i64 = FT_LOAD_DEFAULT}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, +{"no_scale", "set no_scale", 0, AV_OPT_TYPE_CONST, {.i64 = FT_LOAD_NO_SCALE}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, +{"no_hinting", "set no_hinting", 0, AV_OPT_TYPE_CONST, {.i64 = FT_LOAD_NO_HINTING}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, +{"render", "set render", 0, AV_OPT_TYPE_CONST, {.i64 = FT_LOAD_RENDER}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, +{"no_bitmap", "set no_bitmap", 0, AV_OPT_TYPE_CONST, {.i64 = FT_LOAD_NO_BITMAP}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, +{"vertical_layout", "set vertical_layout", 0, AV_OPT_TYPE_CONST, {.i64 = FT_LOAD_VERTICAL_LAYOUT}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, +{"force_autohint", "set force_autohint", 0, AV_OPT_TYPE_CONST, {.i64 = FT_LOAD_FORCE_AUTOHINT}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, +{"crop_bitmap", "set crop_bitmap", 0, AV_OPT_TYPE_CONST, {.i64 = FT_LOAD_CROP_BITMAP}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, +{"pedantic", "set pedantic", 0, AV_OPT_TYPE_CONST, {.i64 = FT_LOAD_PEDANTIC}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, +{"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, 0, "ft_load_flags" }, +{"no_recurse", "set no_recurse", 0, AV_OPT_TYPE_CONST, {.i64 = FT_LOAD_NO_RECURSE}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, +{"ignore_transform", "set ignore_transform", 0, AV_OPT_TYPE_CONST, {.i64 = FT_LOAD_IGNORE_TRANSFORM}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, +{"monochrome", "set monochrome", 0, AV_OPT_TYPE_CONST, {.i64 = FT_LOAD_MONOCHROME}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, +{"linear_design", "set linear_design", 0, AV_OPT_TYPE_CONST, {.i64 = FT_LOAD_LINEAR_DESIGN}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, +{"no_autohint", "set no_autohint", 0, AV_OPT_TYPE_CONST, {.i64 = FT_LOAD_NO_AUTOHINT}, INT_MIN, INT_MAX, 0, "ft_load_flags" }, {NULL}, }; @@ -274,7 +281,7 @@ error: return ret; } -static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) +static av_cold int init(AVFilterContext *ctx, const char *args) { int err; DrawTextContext *dtext = ctx->priv; @@ -392,7 +399,7 @@ static int query_formats(AVFilterContext *ctx) PIX_FMT_NONE }; - avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts)); + ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); return 0; } @@ -429,7 +436,7 @@ static av_cold void uninit(AVFilterContext *ctx) static inline int is_newline(uint32_t c) { - return (c == '\n' || c == '\r' || c == '\f' || c == '\v'); + return c == '\n' || c == '\r' || c == '\f' || c == '\v'; } static int dtext_prepare_text(AVFilterContext *ctx) @@ -492,9 +499,11 @@ static int dtext_prepare_text(AVFilterContext *ctx) /* get glyph */ dummy.code = code; glyph = av_tree_find(dtext->glyphs, &dummy, glyph_cmp, NULL); - if (!glyph) + if (!glyph) { ret = load_glyph(ctx, &glyph, code); - if (ret) return ret; + if (ret) + return ret; + } y_min = FFMIN(glyph->bbox.yMin, y_min); y_max = FFMAX(glyph->bbox.yMax, y_max); @@ -548,7 +557,9 @@ static int dtext_prepare_text(AVFilterContext *ctx) y = FFMIN(y + text_height, height - 1); dtext->w = str_w; + dtext->var_values[VAR_TEXT_W] = dtext->var_values[VAR_TW] = dtext->w; dtext->h = y; + dtext->var_values[VAR_TEXT_H] = dtext->var_values[VAR_TH] = dtext->h; return 0; } @@ -781,7 +792,10 @@ static int draw_text(AVFilterContext *ctx, AVFilterBufferRef *picref, return 0; } -static void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { } +static int null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) +{ + return 0; +} static inline int normalize_double(int *n, double d) { @@ -798,15 +812,16 @@ static inline int normalize_double(int *n, double d) return ret; } -static void start_frame(AVFilterLink *inlink, AVFilterBufferRef *inpicref) +static int start_frame(AVFilterLink *inlink, AVFilterBufferRef *inpicref) { AVFilterContext *ctx = inlink->dst; DrawTextContext *dtext = ctx->priv; - int fail = 0; + AVFilterBufferRef *buf_out; + int ret = 0; - if (dtext_prepare_text(ctx) < 0) { + if ((ret = dtext_prepare_text(ctx)) < 0) { av_log(ctx, AV_LOG_ERROR, "Can't draw text\n"); - fail = 1; + return ret; } dtext->var_values[VAR_T] = inpicref->pts == AV_NOPTS_VALUE ? @@ -818,18 +833,19 @@ static void start_frame(AVFilterLink *inlink, AVFilterBufferRef *inpicref) dtext->var_values[VAR_X] = av_expr_eval(dtext->x_pexpr, dtext->var_values, &dtext->prng); - dtext->draw = fail ? 0 : - av_expr_eval(dtext->d_pexpr, dtext->var_values, &dtext->prng); + dtext->draw = av_expr_eval(dtext->d_pexpr, dtext->var_values, &dtext->prng); normalize_double(&dtext->x, dtext->var_values[VAR_X]); normalize_double(&dtext->y, dtext->var_values[VAR_Y]); - if (dtext->x < 0) dtext->x = 0; - if (dtext->y < 0) dtext->y = 0; - if ((unsigned)dtext->x + (unsigned)dtext->w > inlink->w) - dtext->x = inlink->w - dtext->w; - if ((unsigned)dtext->y + (unsigned)dtext->h > inlink->h) - dtext->y = inlink->h - dtext->h; + if (dtext->fix_bounds) { + if (dtext->x < 0) dtext->x = 0; + if (dtext->y < 0) dtext->y = 0; + if ((unsigned)dtext->x + (unsigned)dtext->w > inlink->w) + dtext->x = inlink->w - dtext->w; + if ((unsigned)dtext->y + (unsigned)dtext->h > inlink->h) + dtext->y = inlink->h - dtext->h; + } dtext->x &= ~((1 << dtext->hsub) - 1); dtext->y &= ~((1 << dtext->vsub) - 1); @@ -838,22 +854,29 @@ static void start_frame(AVFilterLink *inlink, AVFilterBufferRef *inpicref) (int)dtext->var_values[VAR_N], dtext->var_values[VAR_T], dtext->x, dtext->y, dtext->x+dtext->w, dtext->y+dtext->h); - avfilter_start_frame(inlink->dst->outputs[0], inpicref); + buf_out = avfilter_ref_buffer(inpicref, ~0); + if (!buf_out) + return AVERROR(ENOMEM); + + return ff_start_frame(inlink->dst->outputs[0], buf_out); } -static void end_frame(AVFilterLink *inlink) +static int end_frame(AVFilterLink *inlink) { AVFilterLink *outlink = inlink->dst->outputs[0]; AVFilterBufferRef *picref = inlink->cur_buf; DrawTextContext *dtext = inlink->dst->priv; + int ret; if (dtext->draw) draw_text(inlink->dst, picref, picref->video->w, picref->video->h); dtext->var_values[VAR_N] += 1.0; - avfilter_draw_slice(outlink, 0, picref->video->h, 1); - avfilter_end_frame(outlink); + if ((ret = ff_draw_slice(outlink, 0, picref->video->h, 1)) < 0 || + (ret = ff_end_frame(outlink)) < 0) + return ret; + return 0; } AVFilter avfilter_vf_drawtext = { @@ -864,18 +887,18 @@ AVFilter avfilter_vf_drawtext = { .uninit = uninit, .query_formats = query_formats, - .inputs = (AVFilterPad[]) {{ .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .get_video_buffer = avfilter_null_get_video_buffer, - .start_frame = start_frame, - .draw_slice = null_draw_slice, - .end_frame = end_frame, - .config_props = config_input, - .min_perms = AV_PERM_WRITE | - AV_PERM_READ, - .rej_perms = AV_PERM_PRESERVE }, - { .name = NULL}}, - .outputs = (AVFilterPad[]) {{ .name = "default", - .type = AVMEDIA_TYPE_VIDEO, }, - { .name = NULL}}, + .inputs = (const AVFilterPad[]) {{ .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .get_video_buffer = ff_null_get_video_buffer, + .start_frame = start_frame, + .draw_slice = null_draw_slice, + .end_frame = end_frame, + .config_props = config_input, + .min_perms = AV_PERM_WRITE | + AV_PERM_READ, + .rej_perms = AV_PERM_PRESERVE }, + { .name = NULL}}, + .outputs = (const AVFilterPad[]) {{ .name = "default", + .type = AVMEDIA_TYPE_VIDEO, }, + { .name = NULL}}, };