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.
... | ... |
@@ -505,6 +505,32 @@ If both text and textfile are specified, an error is thrown. |
505 | 505 |
@item x, y |
506 | 506 |
The offsets where text will be drawn within the video frame. |
507 | 507 |
Relative to the top/left border of the output image. |
508 |
+They accept expressions similar to the @ref{overlay} filter: |
|
509 |
+@table @option |
|
510 |
+ |
|
511 |
+@item x, y |
|
512 |
+the computed values for @var{x} and @var{y}. They are evaluated for |
|
513 |
+each new frame. |
|
514 |
+ |
|
515 |
+@item main_w, main_h |
|
516 |
+main input width and height |
|
517 |
+ |
|
518 |
+@item W, H |
|
519 |
+same as @var{main_w} and @var{main_h} |
|
520 |
+ |
|
521 |
+@item text_w, text_h |
|
522 |
+rendered text width and height |
|
523 |
+ |
|
524 |
+@item w, h |
|
525 |
+same as @var{text_w} and @var{text_h} |
|
526 |
+ |
|
527 |
+@item n |
|
528 |
+the number of frames processed, starting from 0 |
|
529 |
+ |
|
530 |
+@item t |
|
531 |
+timestamp expressed in seconds, NAN if the input timestamp is unknown |
|
532 |
+ |
|
533 |
+@end table |
|
508 | 534 |
|
509 | 535 |
The default value of @var{x} and @var{y} is 0. |
510 | 536 |
|
... | ... |
@@ -1048,6 +1074,7 @@ other parameters is 0. |
1048 | 1048 |
These parameters correspond to the parameters assigned to the |
1049 | 1049 |
libopencv function @code{cvSmooth}. |
1050 | 1050 |
|
1051 |
+@anchor{overlay} |
|
1051 | 1052 |
@section overlay |
1052 | 1053 |
|
1053 | 1054 |
Overlay one video on top of another. |
... | ... |
@@ -31,7 +31,9 @@ |
31 | 31 |
|
32 | 32 |
#include "libavutil/colorspace.h" |
33 | 33 |
#include "libavutil/file.h" |
34 |
+#include "libavutil/eval.h" |
|
34 | 35 |
#include "libavutil/opt.h" |
36 |
+#include "libavutil/mathematics.h" |
|
35 | 37 |
#include "libavutil/parseutils.h" |
36 | 38 |
#include "libavutil/pixdesc.h" |
37 | 39 |
#include "libavutil/tree.h" |
... | ... |
@@ -45,6 +47,36 @@ |
45 | 45 |
#include FT_FREETYPE_H |
46 | 46 |
#include FT_GLYPH_H |
47 | 47 |
|
48 |
+static const char *var_names[] = { |
|
49 |
+ "E", |
|
50 |
+ "PHI", |
|
51 |
+ "PI", |
|
52 |
+ "main_w", "W", ///< width of the main video |
|
53 |
+ "main_h", "H", ///< height of the main video |
|
54 |
+ "text_w", "w", ///< width of the overlay text |
|
55 |
+ "text_h", "h", ///< height of the overlay text |
|
56 |
+ "x", |
|
57 |
+ "y", |
|
58 |
+ "n", ///< number of processed frames |
|
59 |
+ "t", ///< timestamp expressed in seconds |
|
60 |
+ NULL |
|
61 |
+}; |
|
62 |
+ |
|
63 |
+enum var_name { |
|
64 |
+ VAR_E, |
|
65 |
+ VAR_PHI, |
|
66 |
+ VAR_PI, |
|
67 |
+ VAR_MAIN_W, VAR_MW, |
|
68 |
+ VAR_MAIN_H, VAR_MH, |
|
69 |
+ VAR_TEXT_W, VAR_TW, |
|
70 |
+ VAR_TEXT_H, VAR_TH, |
|
71 |
+ VAR_X, |
|
72 |
+ VAR_Y, |
|
73 |
+ VAR_N, |
|
74 |
+ VAR_T, |
|
75 |
+ VAR_VARS_NB |
|
76 |
+}; |
|
77 |
+ |
|
48 | 78 |
typedef struct { |
49 | 79 |
const AVClass *class; |
50 | 80 |
uint8_t *fontfile; ///< font to be used |
... | ... |
@@ -81,6 +113,10 @@ typedef struct { |
81 | 81 |
int pixel_step[4]; ///< distance in bytes between the component of each pixel |
82 | 82 |
uint8_t rgba_map[4]; ///< map RGBA offsets to the positions in the packed RGBA format |
83 | 83 |
uint8_t *box_line[4]; ///< line used for filling the box background |
84 |
+ char *x_expr, *y_expr; |
|
85 |
+ AVExpr *x_pexpr, *y_pexpr; ///< parsed expressions for x and y |
|
86 |
+ double var_values[VAR_VARS_NB]; |
|
87 |
+ int draw; ///< set to zero to prevent drawing |
|
84 | 88 |
} DrawTextContext; |
85 | 89 |
|
86 | 90 |
#define OFFSET(x) offsetof(DrawTextContext, x) |
... | ... |
@@ -94,8 +130,8 @@ static const AVOption drawtext_options[]= { |
94 | 94 |
{"shadowcolor", "set shadow color", OFFSET(shadowcolor_string), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX }, |
95 | 95 |
{"box", "set box", OFFSET(draw_box), AV_OPT_TYPE_INT, {.dbl=0}, 0, 1 }, |
96 | 96 |
{"fontsize", "set font size", OFFSET(fontsize), AV_OPT_TYPE_INT, {.dbl=16}, 1, 72 }, |
97 |
-{"x", "set x", OFFSET(x), AV_OPT_TYPE_INT, {.dbl=0}, 0, INT_MAX }, |
|
98 |
-{"y", "set y", OFFSET(y), AV_OPT_TYPE_INT, {.dbl=0}, 0, INT_MAX }, |
|
97 |
+{"x", "set x", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str="0"}, CHAR_MIN, CHAR_MAX }, |
|
98 |
+{"y", "set y", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str="0"}, CHAR_MIN, CHAR_MAX }, |
|
99 | 99 |
{"shadowx", "set x", OFFSET(shadowx), AV_OPT_TYPE_INT, {.dbl=0}, INT_MIN, INT_MAX }, |
100 | 100 |
{"shadowy", "set y", OFFSET(shadowy), AV_OPT_TYPE_INT, {.dbl=0}, INT_MIN, INT_MAX }, |
101 | 101 |
{"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) |
374 | 374 |
return (c == '\n' || c == '\r' || c == '\f' || c == '\v'); |
375 | 375 |
} |
376 | 376 |
|
377 |
-static int dtext_prepare_text(AVFilterContext *ctx, int width, int height) |
|
377 |
+static int dtext_prepare_text(AVFilterContext *ctx) |
|
378 | 378 |
{ |
379 | 379 |
DrawTextContext *dtext = ctx->priv; |
380 | 380 |
uint32_t code = 0, prev_code = 0; |
... | ... |
@@ -387,6 +423,8 @@ static int dtext_prepare_text(AVFilterContext *ctx, int width, int height) |
387 | 387 |
FT_Vector delta; |
388 | 388 |
Glyph *glyph = NULL, *prev_glyph = NULL; |
389 | 389 |
Glyph dummy = { 0 }; |
390 |
+ int width = ctx->inputs[0]->w; |
|
391 |
+ int height = ctx->inputs[0]->h; |
|
390 | 392 |
|
391 | 393 |
#if HAVE_LOCALTIME_R |
392 | 394 |
time_t now = time(0); |
... | ... |
@@ -504,6 +542,27 @@ static int config_input(AVFilterLink *inlink) |
504 | 504 |
dtext->hsub = pix_desc->log2_chroma_w; |
505 | 505 |
dtext->vsub = pix_desc->log2_chroma_h; |
506 | 506 |
|
507 |
+ dtext->var_values[VAR_E ] = M_E; |
|
508 |
+ dtext->var_values[VAR_PHI] = M_PHI; |
|
509 |
+ dtext->var_values[VAR_PI ] = M_PI; |
|
510 |
+ |
|
511 |
+ dtext->var_values[VAR_MAIN_W] = |
|
512 |
+ dtext->var_values[VAR_MW] = ctx->inputs[0]->w; |
|
513 |
+ dtext->var_values[VAR_MAIN_H] = |
|
514 |
+ dtext->var_values[VAR_MH] = ctx->inputs[0]->h; |
|
515 |
+ |
|
516 |
+ dtext->var_values[VAR_X] = 0; |
|
517 |
+ dtext->var_values[VAR_Y] = 0; |
|
518 |
+ dtext->var_values[VAR_N] = 0; |
|
519 |
+ dtext->var_values[VAR_T] = NAN; |
|
520 |
+ |
|
521 |
+ |
|
522 |
+ if ((ret = av_expr_parse(&dtext->x_pexpr, dtext->x_expr, var_names, |
|
523 |
+ NULL, NULL, NULL, NULL, 0, ctx)) < 0 || |
|
524 |
+ (ret = av_expr_parse(&dtext->y_pexpr, dtext->y_expr, var_names, |
|
525 |
+ NULL, NULL, NULL, NULL, 0, ctx)) < 0) |
|
526 |
+ return AVERROR(EINVAL); |
|
527 |
+ |
|
507 | 528 |
if ((ret = |
508 | 529 |
ff_fill_line_with_color(dtext->box_line, dtext->pixel_step, |
509 | 530 |
inlink->w, dtext->boxcolor, |
... | ... |
@@ -524,7 +583,9 @@ static int config_input(AVFilterLink *inlink) |
524 | 524 |
dtext->shadowcolor[3] = rgba[3]; |
525 | 525 |
} |
526 | 526 |
|
527 |
- return dtext_prepare_text(ctx, ctx->inputs[0]->w, ctx->inputs[0]->h); |
|
527 |
+ dtext->draw = 1; |
|
528 |
+ |
|
529 |
+ return dtext_prepare_text(ctx); |
|
528 | 530 |
} |
529 | 531 |
|
530 | 532 |
#define GET_BITMAP_VAL(r, c) \ |
... | ... |
@@ -697,15 +758,71 @@ static int draw_text(AVFilterContext *ctx, AVFilterBufferRef *picref, |
697 | 697 |
|
698 | 698 |
static void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { } |
699 | 699 |
|
700 |
+static inline int normalize_double(int *n, double d) |
|
701 |
+{ |
|
702 |
+ int ret = 0; |
|
703 |
+ |
|
704 |
+ if (isnan(d)) { |
|
705 |
+ ret = AVERROR(EINVAL); |
|
706 |
+ } else if (d > INT_MAX || d < INT_MIN) { |
|
707 |
+ *n = d > INT_MAX ? INT_MAX : INT_MIN; |
|
708 |
+ ret = AVERROR(EINVAL); |
|
709 |
+ } else |
|
710 |
+ *n = round(d); |
|
711 |
+ |
|
712 |
+ return ret; |
|
713 |
+} |
|
714 |
+ |
|
715 |
+static void start_frame(AVFilterLink *inlink, AVFilterBufferRef *inpicref) |
|
716 |
+{ |
|
717 |
+ AVFilterContext *ctx = inlink->dst; |
|
718 |
+ DrawTextContext *dtext = ctx->priv; |
|
719 |
+ |
|
720 |
+ if (dtext_prepare_text(ctx) < 0) { |
|
721 |
+ av_log(ctx, AV_LOG_ERROR, "Can't draw text\n"); |
|
722 |
+ dtext->draw = 0; |
|
723 |
+ } |
|
724 |
+ |
|
725 |
+ dtext->var_values[VAR_T] = inpicref->pts == AV_NOPTS_VALUE ? |
|
726 |
+ NAN : inpicref->pts * av_q2d(inlink->time_base); |
|
727 |
+ dtext->var_values[VAR_X] = |
|
728 |
+ av_expr_eval(dtext->x_pexpr, dtext->var_values, NULL); |
|
729 |
+ dtext->var_values[VAR_Y] = |
|
730 |
+ av_expr_eval(dtext->y_pexpr, dtext->var_values, NULL); |
|
731 |
+ dtext->var_values[VAR_X] = |
|
732 |
+ av_expr_eval(dtext->x_pexpr, dtext->var_values, NULL); |
|
733 |
+ |
|
734 |
+ normalize_double(&dtext->x, dtext->var_values[VAR_X]); |
|
735 |
+ normalize_double(&dtext->y, dtext->var_values[VAR_Y]); |
|
736 |
+ |
|
737 |
+ if (dtext->x < 0) dtext->x = 0; |
|
738 |
+ if (dtext->y < 0) dtext->y = 0; |
|
739 |
+ if ((unsigned)dtext->x + (unsigned)dtext->w > inlink->w) |
|
740 |
+ dtext->x = inlink->w - dtext->w; |
|
741 |
+ if ((unsigned)dtext->y + (unsigned)dtext->h > inlink->h) |
|
742 |
+ dtext->y = inlink->h - dtext->h; |
|
743 |
+ |
|
744 |
+ dtext->x &= ~((1 << dtext->hsub) - 1); |
|
745 |
+ dtext->y &= ~((1 << dtext->vsub) - 1); |
|
746 |
+ |
|
747 |
+ av_dlog(ctx, "n:%d t:%f x:%d y:%d x+w:%d y+h:%d\n", |
|
748 |
+ (int)dtext->var_values[VAR_N], dtext->var_values[VAR_T], |
|
749 |
+ dtext->x, dtext->y, dtext->x+dtext->w, dtext->y+dtext->h); |
|
750 |
+ |
|
751 |
+ avfilter_start_frame(inlink->dst->outputs[0], inpicref); |
|
752 |
+} |
|
753 |
+ |
|
700 | 754 |
static void end_frame(AVFilterLink *inlink) |
701 | 755 |
{ |
702 | 756 |
AVFilterLink *outlink = inlink->dst->outputs[0]; |
703 | 757 |
AVFilterBufferRef *picref = inlink->cur_buf; |
704 |
- int err = dtext_prepare_text(inlink->dst, |
|
705 |
- picref->video->w, picref->video->h); |
|
706 |
- if (!err) |
|
758 |
+ DrawTextContext *dtext = inlink->dst->priv; |
|
759 |
+ |
|
760 |
+ if (dtext->draw) |
|
707 | 761 |
draw_text(inlink->dst, picref, picref->video->w, picref->video->h); |
708 | 762 |
|
763 |
+ dtext->var_values[VAR_N] += 1.0; |
|
764 |
+ |
|
709 | 765 |
avfilter_draw_slice(outlink, 0, picref->video->h, 1); |
710 | 766 |
avfilter_end_frame(outlink); |
711 | 767 |
} |
... | ... |
@@ -721,7 +838,7 @@ AVFilter avfilter_vf_drawtext = { |
721 | 721 |
.inputs = (AVFilterPad[]) {{ .name = "default", |
722 | 722 |
.type = AVMEDIA_TYPE_VIDEO, |
723 | 723 |
.get_video_buffer = avfilter_null_get_video_buffer, |
724 |
- .start_frame = avfilter_null_start_frame, |
|
724 |
+ .start_frame = start_frame, |
|
725 | 725 |
.draw_slice = null_draw_slice, |
726 | 726 |
.end_frame = end_frame, |
727 | 727 |
.config_props = config_input, |