[FFmpeg-devel] [PATCH] lavfi/drawtext: add the reload option.

Stefano Sabatini stefasab at gmail.com
Sun Dec 2 23:54:41 CET 2012


On date Sunday 2012-12-02 19:59:27 +0100, Nicolas George encoded:
> 
> Signed-off-by: Nicolas George <nicolas.george at normalesup.org>
> ---
>  doc/filters.texi          |    4 ++++
>  libavfilter/vf_drawtext.c |   47 +++++++++++++++++++++++++++++++--------------
>  2 files changed, 37 insertions(+), 14 deletions(-)
> 
> 
> Requested by someone on the users mailing-list.
> 
> 
> diff --git a/doc/filters.texi b/doc/filters.texi
> index e25f548..3a6fa04 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -1964,6 +1964,10 @@ parameter @var{text}.
>  
>  If both @var{text} and @var{textfile} are specified, an error is thrown.
>  
> + at item reload
> +If set to 1, the @var{textfile} will be reloaded before each frame.
> +Be sure to update it atomically, or it may be read partially, or even fail.
> +
>  @item x, y
>  The expressions which specify the offsets where text will be drawn
>  within the video frame. They are relative to the top/left border of the
> diff --git a/libavfilter/vf_drawtext.c b/libavfilter/vf_drawtext.c
> index 5fff9bf..43f9844 100644
> --- a/libavfilter/vf_drawtext.c
> +++ b/libavfilter/vf_drawtext.c
> @@ -167,6 +167,7 @@ typedef struct {
>      AVTimecode  tc;                 ///< timecode context
>      int tc24hmax;                   ///< 1 if timecode is wrapped to 24 hours, 0 otherwise
>      int frame_id;
> +    int reload;                     ///< reload text file for each frame
>  } DrawTextContext;
>  
>  #define OFFSET(x) offsetof(DrawTextContext, x)
> @@ -199,6 +200,7 @@ static const AVOption drawtext_options[]= {
>  {"timecode_rate", "set rate (timecode only)", OFFSET(tc_rate),   AV_OPT_TYPE_RATIONAL, {.dbl=0},          0,  INT_MAX, FLAGS},
>  {"r",        "set rate (timecode only)", OFFSET(tc_rate),        AV_OPT_TYPE_RATIONAL, {.dbl=0},          0,  INT_MAX, FLAGS},
>  {"rate",     "set rate (timecode only)", OFFSET(tc_rate),        AV_OPT_TYPE_RATIONAL, {.dbl=0},          0,  INT_MAX, FLAGS},
> +{"reload",   "reload text file for each frame", OFFSET(reload),  AV_OPT_TYPE_INT,    {.i64=0},            0,        1, FLAGS},
>  {"fix_bounds", "if true, check and fix text coords to avoid clipping", OFFSET(fix_bounds), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS},
>  
>  /* FT_LOAD_* flags */
> @@ -393,6 +395,29 @@ static int load_font(AVFilterContext *ctx)
>      return err;
>  }
>  
> +static int load_textfile(AVFilterContext *ctx)
> +{
> +    DrawTextContext *dtext = ctx->priv;
> +    int err;
> +    uint8_t *textbuf;
> +    size_t textbuf_size;
> +
> +    if ((err = av_file_map(dtext->textfile, &textbuf, &textbuf_size, 0, ctx)) < 0) {
> +        av_log(ctx, AV_LOG_ERROR,
> +               "The text file '%s' could not be read or is empty\n",
> +               dtext->textfile);
> +        return err;
> +    }
> +
> +    if (!(dtext->text = av_realloc(dtext->text, textbuf_size + 1)))
> +        return AVERROR(ENOMEM);
> +    memcpy(dtext->text, textbuf, textbuf_size);
> +    dtext->text[textbuf_size] = 0;
> +    av_file_unmap(textbuf, textbuf_size);
> +
> +    return 0;
> +}
> +
>  static av_cold int init(AVFilterContext *ctx, const char *args)
>  {
>      int err;
> @@ -411,28 +436,18 @@ static av_cold int init(AVFilterContext *ctx, const char *args)
>      }
>  
>      if (dtext->textfile) {
> -        uint8_t *textbuf;
> -        size_t textbuf_size;
> -
>          if (dtext->text) {
>              av_log(ctx, AV_LOG_ERROR,
>                     "Both text and text file provided. Please provide only one\n");
>              return AVERROR(EINVAL);
>          }
> -        if ((err = av_file_map(dtext->textfile, &textbuf, &textbuf_size, 0, ctx)) < 0) {
> -            av_log(ctx, AV_LOG_ERROR,
> -                   "The text file '%s' could not be read or is empty\n",
> -                   dtext->textfile);
> +        if ((err = load_textfile(ctx)) < 0)
>              return err;
> -        }
> -
> -        if (!(dtext->text = av_malloc(textbuf_size+1)))
> -            return AVERROR(ENOMEM);
> -        memcpy(dtext->text, textbuf, textbuf_size);
> -        dtext->text[textbuf_size] = 0;
> -        av_file_unmap(textbuf, textbuf_size);
>      }
>  
> +    if (dtext->reload && !dtext->textfile)
> +        av_log(ctx, AV_LOG_WARNING, "No file to reload\n");
> +
>      if (dtext->tc_opt_string) {
>          int ret = av_timecode_init_from_string(&dtext->tc, dtext->tc_rate,
>                                                 dtext->tc_opt_string, ctx);
> @@ -972,6 +987,10 @@ static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *frame)
>      DrawTextContext *dtext = ctx->priv;
>      int ret;
>  
> +    if (dtext->reload)
> +        if ((ret = load_textfile(ctx)) < 0)
> +            return ret;

My only concern is that reloading a file for each frame (thus possibly
several times in a second) doesn't sound very good.

Alternative solutions: the user injects an event (through a dedicated
event handler filter?)  to reload the textfile, or it sends metadata
which is then read by drawtext (via the expansion mechanism, something
like %{metadata:text}.

But again, I'm not conceptually opposed to this patch, but we should
start to think about a more flexible (and efficient) solution.
-- 
FFmpeg = Fierce and Free Magic Political Extreme Glue


More information about the ffmpeg-devel mailing list