#include <sys/time.h>
#include <time.h>
+#include "config.h"
+#include "libavcodec/timecode.h"
+#include "libavutil/avstring.h"
#include "libavutil/colorspace.h"
-#include "libavutil/eval.h"
#include "libavutil/file.h"
+#include "libavutil/eval.h"
#include "libavutil/opt.h"
+#include "libavutil/random_seed.h"
#include "libavutil/parseutils.h"
#include "libavutil/pixdesc.h"
#include "libavutil/tree.h"
+#include "libavutil/lfg.h"
#include "avfilter.h"
#include "drawutils.h"
#include FT_GLYPH_H
static const char * const var_names[] = {
- "w", ///< width of the input video
- "h", ///< height of the input video
+ "main_w", "w", "W", ///< width of the input video
+ "main_h", "h", "H", ///< height of the input video
"tw", "text_w", ///< width of the rendered text
"th", "text_h", ///< height of the rendered text
"max_glyph_w", ///< max glyph width
NULL
};
+static const char *fun2_names[] = {
+ "rand",
+};
+
+static double drand(void *opaque, double min, double max)
+{
+ return min + (max-min) / UINT_MAX * av_lfg_get(opaque);
+}
+
+typedef double (*eval_func2)(void *, double a, double b);
+
+static const eval_func2 fun2[] = {
+ drand,
+ NULL
+};
+
enum var_name {
- VAR_W,
- VAR_H,
+ VAR_MAIN_W, VAR_w, VAR_W,
+ VAR_MAIN_H, VAR_h, VAR_H,
VAR_TW, VAR_TEXT_W,
VAR_TH, VAR_TEXT_H,
VAR_MAX_GLYPH_W,
char *textfile; ///< file with text to be drawn
int x; ///< x position to start drawing text
int y; ///< y position to start drawing text
- char *x_expr; ///< expression for x position
- char *y_expr; ///< expression for y position
- AVExpr *x_pexpr, *y_pexpr; ///< parsed expressions for x and y
int max_glyph_w; ///< max glyph width
int max_glyph_h; ///< max glyph heigth
int shadowx, shadowy;
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; ///< expression for x position
+ char *y_expr; ///< expression for y position
+ AVExpr *x_pexpr, *y_pexpr; ///< parsed expressions for x and y
int64_t basetime; ///< base pts time in the real world for display
double var_values[VAR_VARS_NB];
+ char *d_expr;
+ AVExpr *d_pexpr;
+ int draw; ///< set to zero to prevent drawing
+ AVLFG prng; ///< random
+ struct ff_timecode tc;
+ int frame_id;
} DrawTextContext;
#define OFFSET(x) offsetof(DrawTextContext, x)
{"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 },
{"basetime", "set base time", OFFSET(basetime), AV_OPT_TYPE_INT64, {.dbl=AV_NOPTS_VALUE}, INT64_MIN, INT64_MAX },
-
+{"draw", "if false do not draw", OFFSET(d_expr), AV_OPT_TYPE_STRING, {.str="1"}, CHAR_MIN, CHAR_MAX },
+{"timecode", "set initial timecode", OFFSET(tc.str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX },
+{"r", "set rate (timecode only)", OFFSET(tc.rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX },
+{"rate", "set rate (timecode only)", OFFSET(tc.rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX },
/* 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" },
av_file_unmap(textbuf, textbuf_size);
}
+ if (dtext->tc.str) {
+#if CONFIG_AVCODEC
+ if (avpriv_init_smpte_timecode(ctx, &dtext->tc) < 0)
+ return AVERROR(EINVAL);
+ if (!dtext->text)
+ dtext->text = av_strdup("");
+#else
+ av_log(ctx, AV_LOG_ERROR,
+ "Timecode options are only available if libavfilter is built with libavcodec enabled.\n");
+ return AVERROR(EINVAL);
+#endif
+ }
+
if (!dtext->text) {
av_log(ctx, AV_LOG_ERROR,
- "Either text or a valid file must be provided\n");
+ "Either text, a valid file or a timecode must be provided\n");
return AVERROR(EINVAL);
}
load_glyph(ctx, NULL, 0);
/* set the tabsize in pixels */
- if ((err = load_glyph(ctx, &glyph, ' ') < 0)) {
+ if ((err = load_glyph(ctx, &glyph, ' ')) < 0) {
av_log(ctx, AV_LOG_ERROR, "Could not set tabsize.\n");
return err;
}
}
}
+static inline int is_newline(uint32_t c)
+{
+ return c == '\n' || c == '\r' || c == '\f' || c == '\v';
+}
+
static int config_input(AVFilterLink *inlink)
{
AVFilterContext *ctx = inlink->dst;
- DrawTextContext *dtext = inlink->dst->priv;
+ DrawTextContext *dtext = ctx->priv;
const AVPixFmtDescriptor *pix_desc = &av_pix_fmt_descriptors[inlink->format];
int ret;
dtext->shadowcolor[3] = rgba[3];
}
- dtext->var_values[VAR_W] = inlink->w;
- dtext->var_values[VAR_H] = inlink->h;
+ dtext->var_values[VAR_w] = dtext->var_values[VAR_W] = dtext->var_values[VAR_MAIN_W] = inlink->w;
+ dtext->var_values[VAR_h] = dtext->var_values[VAR_H] = dtext->var_values[VAR_MAIN_H] = inlink->h;
dtext->var_values[VAR_SAR] = inlink->sample_aspect_ratio.num ? av_q2d(inlink->sample_aspect_ratio) : 1;
dtext->var_values[VAR_DAR] = (double)inlink->w / inlink->h * dtext->var_values[VAR_SAR];
dtext->var_values[VAR_HSUB] = 1<<pix_desc->log2_chroma_w;
dtext->var_values[VAR_N] = 0;
dtext->var_values[VAR_T] = NAN;
+ av_lfg_init(&dtext->prng, av_get_random_seed());
+
if ((ret = av_expr_parse(&dtext->x_pexpr, dtext->x_expr, var_names,
- NULL, NULL, NULL, NULL, 0, ctx)) < 0 ||
+ NULL, NULL, fun2_names, fun2, 0, ctx)) < 0 ||
(ret = av_expr_parse(&dtext->y_pexpr, dtext->y_expr, var_names,
- NULL, NULL, NULL, NULL, 0, ctx)) < 0)
+ NULL, NULL, fun2_names, fun2, 0, ctx)) < 0 ||
+ (ret = av_expr_parse(&dtext->d_pexpr, dtext->d_expr, var_names,
+ NULL, NULL, fun2_names, fun2, 0, ctx)) < 0)
+
return AVERROR(EINVAL);
return 0;
}
}
-static inline int is_newline(uint32_t c)
-{
- return (c == '\n' || c == '\r' || c == '\f' || c == '\v');
-}
-
static int draw_glyphs(DrawTextContext *dtext, AVFilterBufferRef *picref,
int width, int height, const uint8_t rgbcolor[4], const uint8_t yuvcolor[4], int x, int y)
{
buf_size *= 2;
} while ((buf = av_realloc(buf, buf_size)));
+#if CONFIG_AVCODEC
+ if (dtext->tc.str) {
+ char tcbuf[sizeof("hh:mm:ss.ff")];
+ avpriv_timecode_to_string(tcbuf, &dtext->tc, dtext->frame_id++);
+ buf = av_asprintf("%s%s", dtext->text, tcbuf);
+ }
+#endif
+
if (!buf)
return AVERROR(ENOMEM);
text = dtext->expanded_text = buf;
dtext->var_values[VAR_LINE_H] = dtext->var_values[VAR_LH] = dtext->max_glyph_h;
- dtext->x = dtext->var_values[VAR_X] = av_expr_eval(dtext->x_pexpr, dtext->var_values, NULL);
- dtext->y = dtext->var_values[VAR_Y] = av_expr_eval(dtext->y_pexpr, dtext->var_values, NULL);
- dtext->x = dtext->var_values[VAR_X] = av_expr_eval(dtext->x_pexpr, dtext->var_values, NULL);
+ dtext->x = dtext->var_values[VAR_X] = av_expr_eval(dtext->x_pexpr, dtext->var_values, &dtext->prng);
+ dtext->y = dtext->var_values[VAR_Y] = av_expr_eval(dtext->y_pexpr, dtext->var_values, &dtext->prng);
+ dtext->x = dtext->var_values[VAR_X] = av_expr_eval(dtext->x_pexpr, dtext->var_values, &dtext->prng);
+ dtext->draw = av_expr_eval(dtext->d_pexpr, dtext->var_values, &dtext->prng);
+
+ if(!dtext->draw)
+ return 0;
dtext->x &= ~((1 << dtext->hsub) - 1);
dtext->y &= ~((1 << dtext->vsub) - 1);
/* draw box */
if (dtext->draw_box)
drawbox(picref, dtext->x, dtext->y, box_w, box_h,
- dtext->box_line, dtext->pixel_step, dtext->boxcolor_rgba,
+ dtext->box_line, dtext->pixel_step, dtext->is_packed_rgb ? dtext->boxcolor_rgba : dtext->boxcolor,
dtext->hsub, dtext->vsub, dtext->is_packed_rgb, dtext->rgba_map);
if (dtext->shadowx || dtext->shadowy) {