From 2cf74eca70759320200f06efe4a09bd69e169dec Mon Sep 17 00:00:00 2001 From: Luca Barbato Date: Thu, 1 Dec 2011 11:43:11 +0100 Subject: drawtext: make x and y parametric Introduce variables "E", "PHI", "PI", "main_w"/"W", "main_h"/"H", "text_w"/"w", "text_h"/"h", "x", "y", "n" and "t" in line with vf_overlay and refactor the code accordingly. --- libavfilter/vf_drawtext.c | 133 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 125 insertions(+), 8 deletions(-) (limited to 'libavfilter/vf_drawtext.c') diff --git a/libavfilter/vf_drawtext.c b/libavfilter/vf_drawtext.c index 06b5dc0ba1..b7ba89c944 100644 --- a/libavfilter/vf_drawtext.c +++ b/libavfilter/vf_drawtext.c @@ -31,7 +31,9 @@ #include "libavutil/colorspace.h" #include "libavutil/file.h" +#include "libavutil/eval.h" #include "libavutil/opt.h" +#include "libavutil/mathematics.h" #include "libavutil/parseutils.h" #include "libavutil/pixdesc.h" #include "libavutil/tree.h" @@ -45,6 +47,36 @@ #include FT_FREETYPE_H #include FT_GLYPH_H +static const char *var_names[] = { + "E", + "PHI", + "PI", + "main_w", "W", ///< width of the main video + "main_h", "H", ///< height of the main video + "text_w", "w", ///< width of the overlay text + "text_h", "h", ///< height of the overlay text + "x", + "y", + "n", ///< number of processed frames + "t", ///< timestamp expressed in seconds + NULL +}; + +enum var_name { + VAR_E, + VAR_PHI, + VAR_PI, + VAR_MAIN_W, VAR_MW, + VAR_MAIN_H, VAR_MH, + VAR_TEXT_W, VAR_TW, + VAR_TEXT_H, VAR_TH, + VAR_X, + VAR_Y, + VAR_N, + VAR_T, + VAR_VARS_NB +}; + typedef struct { const AVClass *class; uint8_t *fontfile; ///< font to be used @@ -81,6 +113,10 @@ typedef struct { int pixel_step[4]; ///< distance in bytes between the component of each pixel uint8_t rgba_map[4]; ///< map RGBA offsets to the positions in the packed RGBA format uint8_t *box_line[4]; ///< line used for filling the box background + char *x_expr, *y_expr; + AVExpr *x_pexpr, *y_pexpr; ///< parsed expressions for x and y + double var_values[VAR_VARS_NB]; + int draw; ///< set to zero to prevent drawing } DrawTextContext; #define OFFSET(x) offsetof(DrawTextContext, x) @@ -94,8 +130,8 @@ static const AVOption drawtext_options[]= { {"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 }, -{"x", "set x", OFFSET(x), AV_OPT_TYPE_INT, {.dbl=0}, 0, INT_MAX }, -{"y", "set y", OFFSET(y), AV_OPT_TYPE_INT, {.dbl=0}, 0, INT_MAX }, +{"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 }, @@ -374,7 +410,7 @@ static inline int is_newline(uint32_t c) return (c == '\n' || c == '\r' || c == '\f' || c == '\v'); } -static int dtext_prepare_text(AVFilterContext *ctx, int width, int height) +static int dtext_prepare_text(AVFilterContext *ctx) { DrawTextContext *dtext = ctx->priv; uint32_t code = 0, prev_code = 0; @@ -387,6 +423,8 @@ static int dtext_prepare_text(AVFilterContext *ctx, int width, int height) FT_Vector delta; Glyph *glyph = NULL, *prev_glyph = NULL; Glyph dummy = { 0 }; + int width = ctx->inputs[0]->w; + int height = ctx->inputs[0]->h; #if HAVE_LOCALTIME_R time_t now = time(0); @@ -504,6 +542,27 @@ static int config_input(AVFilterLink *inlink) dtext->hsub = pix_desc->log2_chroma_w; dtext->vsub = pix_desc->log2_chroma_h; + dtext->var_values[VAR_E ] = M_E; + dtext->var_values[VAR_PHI] = M_PHI; + dtext->var_values[VAR_PI ] = M_PI; + + dtext->var_values[VAR_MAIN_W] = + dtext->var_values[VAR_MW] = ctx->inputs[0]->w; + dtext->var_values[VAR_MAIN_H] = + dtext->var_values[VAR_MH] = ctx->inputs[0]->h; + + dtext->var_values[VAR_X] = 0; + dtext->var_values[VAR_Y] = 0; + dtext->var_values[VAR_N] = 0; + dtext->var_values[VAR_T] = NAN; + + + if ((ret = av_expr_parse(&dtext->x_pexpr, dtext->x_expr, var_names, + NULL, NULL, NULL, NULL, 0, ctx)) < 0 || + (ret = av_expr_parse(&dtext->y_pexpr, dtext->y_expr, var_names, + NULL, NULL, NULL, NULL, 0, ctx)) < 0) + return AVERROR(EINVAL); + if ((ret = ff_fill_line_with_color(dtext->box_line, dtext->pixel_step, inlink->w, dtext->boxcolor, @@ -524,7 +583,9 @@ static int config_input(AVFilterLink *inlink) dtext->shadowcolor[3] = rgba[3]; } - return dtext_prepare_text(ctx, ctx->inputs[0]->w, ctx->inputs[0]->h); + dtext->draw = 1; + + return dtext_prepare_text(ctx); } #define GET_BITMAP_VAL(r, c) \ @@ -697,15 +758,71 @@ static int draw_text(AVFilterContext *ctx, AVFilterBufferRef *picref, static void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { } +static inline int normalize_double(int *n, double d) +{ + int ret = 0; + + if (isnan(d)) { + ret = AVERROR(EINVAL); + } else if (d > INT_MAX || d < INT_MIN) { + *n = d > INT_MAX ? INT_MAX : INT_MIN; + ret = AVERROR(EINVAL); + } else + *n = round(d); + + return ret; +} + +static void start_frame(AVFilterLink *inlink, AVFilterBufferRef *inpicref) +{ + AVFilterContext *ctx = inlink->dst; + DrawTextContext *dtext = ctx->priv; + + if (dtext_prepare_text(ctx) < 0) { + av_log(ctx, AV_LOG_ERROR, "Can't draw text\n"); + dtext->draw = 0; + } + + dtext->var_values[VAR_T] = inpicref->pts == AV_NOPTS_VALUE ? + NAN : inpicref->pts * av_q2d(inlink->time_base); + dtext->var_values[VAR_X] = + av_expr_eval(dtext->x_pexpr, dtext->var_values, NULL); + dtext->var_values[VAR_Y] = + av_expr_eval(dtext->y_pexpr, dtext->var_values, NULL); + dtext->var_values[VAR_X] = + av_expr_eval(dtext->x_pexpr, dtext->var_values, NULL); + + 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; + + dtext->x &= ~((1 << dtext->hsub) - 1); + dtext->y &= ~((1 << dtext->vsub) - 1); + + av_dlog(ctx, "n:%d t:%f x:%d y:%d x+w:%d y+h:%d\n", + (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); +} + static void end_frame(AVFilterLink *inlink) { AVFilterLink *outlink = inlink->dst->outputs[0]; AVFilterBufferRef *picref = inlink->cur_buf; - int err = dtext_prepare_text(inlink->dst, - picref->video->w, picref->video->h); - if (!err) + DrawTextContext *dtext = inlink->dst->priv; + + 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); } @@ -721,7 +838,7 @@ AVFilter avfilter_vf_drawtext = { .inputs = (AVFilterPad[]) {{ .name = "default", .type = AVMEDIA_TYPE_VIDEO, .get_video_buffer = avfilter_null_get_video_buffer, - .start_frame = avfilter_null_start_frame, + .start_frame = start_frame, .draw_slice = null_draw_slice, .end_frame = end_frame, .config_props = config_input, -- cgit v1.2.3