[FFmpeg-devel] [PATCH] Add drawtext filter from the libavfilter soc repo.

Måns Rullgård mans
Fri Feb 18 15:54:45 CET 2011


Stefano Sabatini <stefano.sabatini-lala at poste.it> writes:

> On date Tuesday 2010-10-05 02:09:16 -0700, Baptiste Coudurier encoded:
>> On 10/5/10 2:03 AM, Stefano Sabatini wrote:
>> > On date Tuesday 2010-09-28 16:17:00 +0200, Stefano Sabatini encoded:
>> >> ---
>> >>  configure                 |    6 +
>> >>  doc/filters.texi          |   81 ++++++++
>> >>  libavfilter/Makefile      |    1 +
>> >>  libavfilter/allfilters.c  |    1 +
>> >>  libavfilter/vf_drawtext.c |  494 +++++++++++++++++++++++++++++++++++++++++++++
>> >>  5 files changed, 583 insertions(+), 0 deletions(-)
>> >>  create mode 100644 libavfilter/vf_drawtext.c
>> > 
>> > Ping.
>> 
>> MONOCHROME is very low quality, and this filter could use draw_slice IMHO.
>
> Update work in progress, gray-tone glyph rendering support added, also
> added a mapping for the libfreetype flags (which is maybe overkill),
> comments are welcome.
>
> Per-slice drawing is a bit tricky so I'd rather avoid it for the first
> round.
>
> I'll add UTF-8 support in a further patch (BTW, do we have UTF-8
> handling utilities in libav*?).

There should be some.  Look for GET_UTF8.

> Also I plan to add support for frame-information printing overlay (for
> example for printing number of frame and PTS, frame-type), but I'm not
> sure how to implement such interface.
> I was thinking about something of the kind:
> drawtext=fontfile=FreeSerif.ttf: mode=frameinfo: text='pts:%p time:%t frame_number:%n': fgcolor=white: fontsize=50
>
> where 'mode' specifies the "template mode" to adopt when "interpreting"
> the passed text, in our case it may be either 'strprintf'
> or 'frameinfo'.
>
> Alternatively we could have different variant of the same filter,
> e.g. drawtext and drawframeinfo, both sharing most of the code.
> -- 
> FFmpeg = Freak & Fast Mournful Programmable Ecumenical God
>
> From 4260c958a8e36a1b2a5bf0b428dfacdd28f99a10 Mon Sep 17 00:00:00 2001
> From: S.N. Hemanth Meenakshisundaram <smeenaks at ucsd.edu>
> Date: Tue, 28 Sep 2010 16:09:27 +0200
> Subject: [PATCH] lavfi: add drawtext filter
>
> Port drawtext filter from the libavfilter soc repo, with some
> additions by Stefano:
> * support to generic load libfreetype options
> * support to anti-aliased glyph rendering
> ---
>  configure                 |    6 +
>  doc/filters.texi          |   81 +++++++
>  libavfilter/Makefile      |    1 +
>  libavfilter/allfilters.c  |    1 +
>  libavfilter/vf_drawtext.c |  530 +++++++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 619 insertions(+), 0 deletions(-)
>  create mode 100644 libavfilter/vf_drawtext.c
>
> diff --git a/configure b/configure
> index ed2934d..b4a8287 100755
> --- a/configure
> +++ b/configure

[...]

> @@ -2821,6 +2826,7 @@ enabled libdirac   && add_cflags $(pkg-config --cflags dirac) &&
>                        require  libdirac libdirac_decoder/dirac_parser.h dirac_decoder_init $(pkg-config --libs dirac) &&
>                        require  libdirac libdirac_encoder/dirac_encoder.h dirac_encoder_init $(pkg-config --libs dirac)
>  enabled libfaac    && require2 libfaac "stdint.h faac.h" faacEncGetVersion -lfaac
> +enabled libfreetype && add_cflags $(pkg-config --cflags freetype2) && require libfreetype ft2build.h FT_Init_FreeType -lfreetype -lz

We already add -lz if available, adding it again here shouldn't be needed.

>  enabled libgsm     && require  libgsm gsm/gsm.h gsm_create -lgsm
>  enabled libmp3lame && require  "libmp3lame >= 3.98.3" lame/lame.h lame_set_VBR_quality -lmp3lame
>  enabled libnut     && require  libnut libnut.h nut_demuxer_init -lnut

[...]

> diff --git a/libavfilter/vf_drawtext.c b/libavfilter/vf_drawtext.c
> new file mode 100644
> index 0000000..9e66f55
> --- /dev/null
> +++ b/libavfilter/vf_drawtext.c
> @@ -0,0 +1,530 @@
> +/*
> + * Copyright (c) 2011 Stefano Sabatini
> + * Copyright (c) 2010 S.N. Hemanth Meenakshisundaram
> + * Copyright (c) 2003 Gustavo Sverzut Barbieri <gsbarbieri at yahoo.com.br>
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +/**
> + * @file
> + * drawtext filter, based on the original FFmpeg vhook/drawtext.c
> + * filter by Gustavo Sverzut Barbieri
> + */
> +
> +#include <stdint.h>
> +#include <stdio.h>
> +
> +#include "libavutil/colorspace.h"
> +#include "libavutil/file.h"
> +#include "libavutil/opt.h"
> +#include "libavutil/parseutils.h"
> +#include "libavutil/pixdesc.h"
> +#include "avfilter.h"
> +
> +#undef time
> +#include <sys/time.h>
> +#include <time.h>

Please place those includes together with the std* ones above.  The
#undef obviously has to stay

> +#include <ft2build.h>
> +#include <freetype/config/ftheader.h>
> +#include FT_FREETYPE_H
> +#include FT_GLYPH_H


Arrrgh, I hate freetype header lunacy.  Not your fault, of course.

> +typedef struct {
> +    const AVClass *class;
> +    unsigned char *fontfile;        ///< font to be used
> +    unsigned char *text;            ///< text to be drawn
> +    char *textfile;                 ///< file with text to be drawn
> +    unsigned int x;                 ///< x position to start drawing text
> +    unsigned int y;                 ///< y position to start drawing text
> +    unsigned int fontsize;          ///< font size to use
> +    char *fgcolor_string;           ///< foreground color as string
> +    char *bgcolor_string;           ///< background color as string
> +    unsigned char fgcolor[4];       ///< foreground color in YUV
> +    unsigned char bgcolor[4];       ///< background/Box color in YUV
> +    short int draw_box;             ///< draw box around text - true or false
> +    short int outline;              ///< draw outline in bg color around text
> +    int text_height;                ///< height of a font symbol
> +    int baseline;                   ///< baseline to draw fonts from
> +    int use_kerning;                ///< font kerning is used - true/false
> +    FT_Library library;             ///< freetype font library handle
> +    FT_Face face;                   ///< freetype font face handle
> +    FT_Glyph glyphs[256];           ///< array holding glyphs of font
> +    FT_Bitmap bitmaps[256];         ///< array holding bitmaps of font
> +    int advance[256];
> +    int bitmap_left[256];
> +    int bitmap_top[256];
> +    unsigned int glyphs_index[256];
> +    int hsub, vsub;                 ///< chroma subsampling values
> +    int ftload_flags;               ///< flags used for loading fonts, see FT_LOAD_*
> +} DrawTextContext;
> +
> +#define OFFSET(x) offsetof(DrawTextContext, x)
> +
> +static const AVOption drawtext_options[]= {

[...]

> +};
> +
> +static const char *drawtext_get_name(void *ctx)
> +{
> +    return "drawtext";
> +}
> +
> +static const AVClass drawtext_class = {
> +    "DrawTextContext",
> +    drawtext_get_name,
> +    drawtext_options
> +};
> +
> +static int query_formats(AVFilterContext *ctx)
> +{
> +    /* FIXME: Add support for other formats */
> +    enum PixelFormat pix_fmts[] = {
> +        PIX_FMT_YUV420P, PIX_FMT_YUV444P, PIX_FMT_YUV422P,
> +        PIX_FMT_YUV411P, PIX_FMT_YUV410P,
> +        PIX_FMT_YUV440P, PIX_FMT_NONE
> +    };
> +
> +    avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts));
> +    return 0;
> +}
> +
> +#undef __FTERRORS_H__
> +#define FT_ERROR_START_LIST {
> +#define FT_ERRORDEF(e, v, s) { (e), (s) },
> +#define FT_ERROR_END_LIST { 0, NULL } };
> +
> +struct ft_error
> +{
> +    int err;
> +    const char *err_msg;
> +} static ft_errors[] =
> +#include FT_ERRORS_H
> +
> +#define FT_ERRMSG(e) ft_errors[e].err_msg

Freetype never ceases to amaze/amuse me with its contorted header file
arrangements...

> +#define MAX_TEXT_SIZE 1024
> +
> +static inline int extract_color(AVFilterContext *ctx, char *color_str, unsigned char *color)
> +{
> +    uint8_t rgba[4];
> +    uint8_t err;
> +    if ((err = av_parse_color(rgba, color_str, -1, ctx)))
> +        return err;
> +
> +    color[0] = RGB_TO_Y(rgba[0], rgba[1], rgba[2]);
> +    color[1] = RGB_TO_U(rgba[0], rgba[1], rgba[2], 0);
> +    color[2] = RGB_TO_V(rgba[0], rgba[1], rgba[2], 0);
> +    color[3] = rgba[3];
> +
> +    return 0;
> +}
> +
> +static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
> +{
> +    unsigned int c;
> +    int err;
> +    int y_max, y_min;
> +    FT_BBox bbox;
> +    DrawTextContext *dtext = ctx->priv;
> +
> +    dtext->class = &drawtext_class;
> +    av_opt_set_defaults2(dtext, 0, 0);
> +    dtext->fgcolor_string = av_strdup("black");
> +    dtext->bgcolor_string = av_strdup("white");
> +
> +    if ((err = (av_set_options_string(dtext, args, "=", ":"))) < 0) {
> +        av_log(ctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args);
> +        return err;
> +    }
> +
> +    if (!dtext->fontfile) {
> +        av_log(ctx, AV_LOG_ERROR,
> +               "No font filename provided\n");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    if (dtext->textfile) {
> +        uint8_t *textbuf;
> +        size_t textbuf_size;
> +
> +        if (dtext->text) {
> +            av_log(ctx, AV_LOG_ERROR,
> +                   "Both text and 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);
> +            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->text) {
> +        av_log(ctx, AV_LOG_ERROR,
> +               "Either text or a valid file must be provided\n");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    if ((err = extract_color(ctx, dtext->fgcolor_string, dtext->fgcolor))) {
> +        av_log(ctx, AV_LOG_ERROR,
> +               "Invalid foreground color: '%s'.\n", dtext->fgcolor_string);
> +        return err;
> +    }
> +
> +    if ((err = extract_color(ctx, dtext->bgcolor_string, dtext->bgcolor))) {
> +        av_log(ctx, AV_LOG_ERROR,
> +               "Invalid background color: '%s'.\n", dtext->fgcolor_string);
> +        return err;
> +    }
> +
> +    if ((err = FT_Init_FreeType(&(dtext->library)))) {
> +        av_log(ctx, AV_LOG_ERROR,
> +               "Could not load FreeType: %s\n", FT_ERRMSG(err));
> +        return AVERROR(EINVAL);
> +    }
> +
> +    /* load the face, and set up the encoding, which is by default UTF-8 */
> +    if ((err = FT_New_Face(dtext->library, dtext->fontfile, 0, &(dtext->face)))) {
> +        av_log(ctx, AV_LOG_ERROR,
> +               "Could not load fontface from file '%s': %s\n", dtext->fontfile, FT_ERRMSG(err));
> +        return AVERROR(EINVAL);
> +    }
> +    if ((err = FT_Set_Pixel_Sizes(dtext->face, 0, dtext->fontsize))) {
> +        av_log(ctx, AV_LOG_ERROR,
> +               "Could not set font size to %d pixels: %s\n", dtext->fontsize, FT_ERRMSG(err));
> +        return AVERROR(EINVAL);
> +    }
> +
> +    dtext->use_kerning = FT_HAS_KERNING(dtext->face);
> +
> +    /* load and cache glyphs */
> +    y_max = -32000;
> +    y_min =  32000;
> +
> +    /* FIXME: Supports only ASCII text now. Add Unicode support */
> +    for (c = 0; c < 256; c++) {
> +        /* Load char */
> +        err = FT_Load_Char(dtext->face, c, dtext->ftload_flags);
> +        if (err)
> +            continue;  /* ignore errors */
> +
> +        dtext->bitmaps    [c] = dtext->face->glyph->bitmap;
> +        dtext->bitmap_left[c] = dtext->face->glyph->bitmap_left;
> +        dtext->bitmap_top [c] = dtext->face->glyph->bitmap_top;
> +        dtext->advance    [c] = dtext->face->glyph->advance.x >> 6;
> +
> +        err = FT_Get_Glyph(dtext->face->glyph, &(dtext->glyphs[c]));
> +        if (err)
> +            continue;  /* ignore errors */
> +
> +        dtext->glyphs_index[c] = FT_Get_Char_Index(dtext->face, c);
> +        //dtext->glyphs_index[c] = glyph_index;
> +
> +        /* Measure text height to calculate text_height (or the maximum text height) */
> +        FT_Glyph_Get_CBox(dtext->glyphs[c], ft_glyph_bbox_pixels, &bbox);
> +        if (bbox.yMax > y_max)
> +          y_max = bbox.yMax;
> +        if (bbox.yMin < y_min)
> +          y_min = bbox.yMin;
> +    }

This caching will have to be changed for utf-8 support, and I imagine a
lot of users of the filter will want that.  It's up to you to prioritise
of course.

> +    dtext->text_height = y_max - y_min;
> +    dtext->baseline = y_max;
> +
> +    return 0;
> +}
> +
> +static av_cold void uninit(AVFilterContext *ctx)
> +{
> +    DrawTextContext *dtext = ctx->priv;
> +    av_free(dtext->fontfile);
> +    av_free(dtext->text);
> +    av_free(dtext->textfile);
> +    av_free(dtext->fgcolor_string);
> +    av_free(dtext->bgcolor_string);
> +    FT_Done_Face(dtext->face);
> +    FT_Done_FreeType(dtext->library);
> +}
> +
> +static int config_input(AVFilterLink *inlink)
> +{
> +    DrawTextContext *dtext = inlink->dst->priv;
> +    const AVPixFmtDescriptor *pix_desc = &av_pix_fmt_descriptors[inlink->format];
> +    dtext->hsub = pix_desc->log2_chroma_w;
> +    dtext->vsub = pix_desc->log2_chroma_h;
> +    return 0;
> +}
> +
> +#define SET_PIXEL(picref, yuv_color, val, x, y, hsub, vsub) {           \
> +    luma_pos    = ((x)          ) + ((y)          ) * picref->linesize[0]; \
> +    chroma_pos1 = ((x) >> (hsub)) + ((y) >> (vsub)) * picref->linesize[1]; \
> +    chroma_pos2 = ((x) >> (hsub)) + ((y) >> (vsub)) * picref->linesize[2]; \
> +    alpha = (yuv_color[3] * (val)) >> 8;                              \
> +    picref->data[0][luma_pos   ] = (alpha * yuv_color[0] + (255 - alpha) * picref->data[0][luma_pos   ]) >> 8; \
> +    picref->data[1][chroma_pos1] = (alpha * yuv_color[1] + (255 - alpha) * picref->data[1][chroma_pos1]) >> 8; \
> +    picref->data[2][chroma_pos2] = (alpha * yuv_color[2] + (255 - alpha) * picref->data[2][chroma_pos2]) >> 8; \
> +}

Strictly speaking, the >>8 is wrong, but I guess the difference is small
enough that it doesn't matter.

> +#define GET_PIXEL(dst_yuv_color, picref, x, y, hsub, vsub) {            \
> +    dst_yuv_color[0] = picref->data[0][ (x)            +  (y)            * picref->linesize[0]]; \
> +    dst_yuv_color[1] = picref->data[1][((x) >> (hsub)) + ((y) >> (vsub)) * picref->linesize[1]]; \
> +    dst_yuv_color[2] = picref->data[2][((x) >> (hsub)) + ((y) >> (vsub)) * picref->linesize[2]]; \
> +}
> +
> +#define GET_BITMAP_PIXEL(r, c)                                          \
> +    bitmap->pixel_mode == FT_PIXEL_MODE_MONO ?                          \
> +        (bitmap->buffer[(r) * bitmap->pitch + (c)/8] & (0x80 >> ((c)%8))) * 255 : \
> +        bitmap->buffer[(r) * bitmap->pitch + c]                         \

This is called with a signed "c", so those /8 and %8 will be slow.
Better to use >>3 and &7.

> +static inline void draw_glyph(AVFilterBufferRef *picref, FT_Bitmap *bitmap, unsigned int x,
> +                              unsigned int y, unsigned int width, unsigned int height,
> +                              unsigned char yuv_fgcolor[4], unsigned char yuv_bgcolor[4],
> +                              short int outline, int hsub, int vsub)
> +{
> +    int r, c, alpha;
> +    unsigned int luma_pos, chroma_pos1, chroma_pos2;
> +    uint8_t src_pixel, dst_pixel[4], in_glyph = 0;
> +
> +    if (bitmap->pixel_mode == FT_PIXEL_MODE_MONO || bitmap->pixel_mode == FT_PIXEL_MODE_GRAY) {
> +        in_glyph = 0;
> +        for (r = 0; (r < bitmap->rows) && ((r+y) < height); r++) {
> +            for (c = 0; (c < bitmap->width) && ((c+x) < width); c++) {

Lots of unnecessary () there.  Getting rid of them would make it easier
to read.  Same in other places.

> +                /* pixel in the picref (destination) */
> +                GET_PIXEL(dst_pixel, picref, c+x, y+r, hsub, vsub);

This seems to be overwritten in quite a few cases below.  Perhaps fetch
it only when actually needed.

> +                /* pixel in the glyph bitmap (source) */
> +                src_pixel = GET_BITMAP_PIXEL(r, c);
> +
> +                if (src_pixel)
> +                    memcpy(dst_pixel, yuv_fgcolor, 4);
> +
> +                if (outline) {
> +                    /* border detection: */
> +                    if (!in_glyph && src_pixel) {
> +                        /* left border detected */
> +                        in_glyph = 1;
> +                        /* draw left pixel border */
> +                        if (c-1 >= 0)
> +                            SET_PIXEL(picref, yuv_bgcolor, src_pixel, c+x-1, y+r, hsub, vsub);
> +                    } else if (in_glyph && !src_pixel) {
> +                    /* right border detected */
> +                        in_glyph = 0;
> +                        /* 'draw' right pixel border */
> +                        memcpy(dst_pixel, yuv_bgcolor, 4);
> +                    }
> +
> +                    if (in_glyph) {
> +                    /* see if we have a top/bottom border */
> +                        /* top border detection */
> +                        if (((r-1) >= 0) && (!(src_pixel = GET_BITMAP_PIXEL(r-1, c))))
> +                            /* draw top border */
> +                            SET_PIXEL(picref, yuv_bgcolor, src_pixel, c+x, y+r-1, hsub, vsub);
> +
> +                        /* bottom border detection */
> +                        if (((r+1) < height) && (!(src_pixel = GET_BITMAP_PIXEL(r+1, c))))
> +                            /* draw bottom border */
> +                            SET_PIXEL(picref, yuv_bgcolor, src_pixel, c+x, y+r+1, hsub, vsub);
> +                    }
> +                }
> +                SET_PIXEL(picref, dst_pixel, src_pixel, c+x, y+r, hsub, vsub);
> +            }
> +        }
> +    }
> +}
> +
> +static inline void drawbox(AVFilterBufferRef *picref, unsigned int x, unsigned int y,
> +                           unsigned int width, unsigned int height,
> +                           unsigned char yuv_color[4], int hsub, int vsub)
> +{
> +    int i, plane, alpha;
> +    uint8_t *p;
> +
> +    if (yuv_color[3] != 0xFF) {
> +        unsigned int j, luma_pos, chroma_pos1, chroma_pos2;
> +
> +        for (j = 0; j < height; j++)
> +            for (i = 0; i < width; i++)
> +                SET_PIXEL(picref, yuv_color, 255, (i+x), (y+j), hsub, vsub);
> +
> +    } else {
> +        for (plane = 0; plane < 3 && picref->data[plane]; plane++) {
> +            int hsub1 = plane == 1 || plane == 2 ? hsub : 0;
> +            int vsub1 = plane == 1 || plane == 2 ? vsub : 0;
> +
> +            p = picref->data[plane] +
> +                (y >> vsub1) * picref->linesize[plane] + (x >> hsub1);
> +            for (i = 0; i < (height >> vsub1); i++) {
> +                memset(p, yuv_color[plane], (width >> hsub1));
> +                p += picref->linesize[plane];
> +            }
> +        }
> +    }
> +}
> +
> +static void draw_text(AVFilterContext *ctx, AVFilterBufferRef *picref,
> +                      int width, int height)
> +{
> +    DrawTextContext *dtext = ctx->priv;
> +    FT_Face face = dtext->face;
> +    FT_GlyphSlot  slot = face->glyph;
> +    unsigned char *text = dtext->text;
> +    unsigned char c;
> +    unsigned char buff[MAX_TEXT_SIZE];
> +    int x = 0, y = 0, i = 0, size = 0;
> +    time_t now = time(0);
> +    int str_w, str_w_max;
> +    FT_Vector pos[MAX_TEXT_SIZE];
> +    FT_Vector delta;
> +    struct tm ltime;

This uses a lot of stack space.  Might be better to put those buffers in
the context instead.

> +#if HAVE_LOCALTIME_R
> +    strftime(buff, sizeof(buff), text, localtime_r(&now, &ltime));
> +    text = buff;
> +#else
> +    av_log(ctx, AV_LOG_WARNING, "strftime() expansion unavailable!");

Printing this warning for every frame seems a bit excessive.

> +#endif
> +    size = strlen(text);
> +
> +    /* measure text size and save glyphs position*/
> +    str_w = str_w_max = 0;
> +    x = dtext->x;
> +    y = dtext->y;
> +    for (i = 0; i < size; i++) {
> +        c = text[i];
> +        /* kerning */
> +        if (dtext->use_kerning && (i > 0) && dtext->glyphs_index[c]) {
> +            FT_Get_Kerning(dtext->face, dtext->glyphs_index[text[i-1]],
> +                           dtext->glyphs_index[c], ft_kerning_default, &delta);
> +            x += delta.x >> 6;
> +        }
> +
> +        if (((x + dtext->advance[c]) >= width) || (c == '\n')) {
> +            if (c != '\n')
> +                str_w_max = width - dtext->x - 1;
> +            y += dtext->text_height;
> +            x = dtext->x;
> +        }
> +
> +        /* save position */
> +        pos[i].x = x + dtext->bitmap_left[c];
> +        pos[i].y = y - dtext->bitmap_top [c] + dtext->baseline;
> +        x     += dtext->advance[c];
> +        str_w += dtext->advance[c];
> +    }
> +    y += dtext->text_height;
> +    if (str_w_max == 0)
> +        str_w_max = str_w;
> +    if (dtext->draw_box) {
> +        /* Check if it doesn't pass the limits */
> +        if (str_w_max + dtext->x >= width)
> +            str_w_max = width - dtext->x - 1;
> +        if (y >= height)
> +            y = height - 1;

FFMIN()

> +        /* draw background */
> +        drawbox(picref, dtext->x, dtext->y, str_w_max, y-dtext->y,
> +                dtext->bgcolor, dtext->hsub, dtext->vsub);
> +    }
> +
> +    /* draw glyphs */
> +    for (i = 0; i < size; i++) {
> +        c = text[i];
> +
> +        /* skip new line char, just go to new line */
> +        if (c == '\n')
> +            continue;

Might be a good idea to skip \r as well.

> +        /* now, draw to our target surface */
> +        draw_glyph(picref, &(dtext->bitmaps[c]), pos[i].x, pos[i].y, width, height,
> +                   dtext->fgcolor, dtext->bgcolor, dtext->outline,
> +                   dtext->hsub, dtext->vsub);
> +
> +        /* increment pen position */
> +        x += slot->advance.x >> 6;
> +    }
> +}
> +
> +static void end_frame(AVFilterLink *inlink)
> +{
> +    AVFilterLink *outlink = inlink->dst->outputs[0];
> +    AVFilterBufferRef *picref = inlink->cur_buf;
> +
> +    draw_text(inlink->dst, picref, picref->video->w, picref->video->h);
> +
> +    avfilter_draw_slice(outlink, 0, picref->video->h, 1);
> +    avfilter_end_frame(outlink);
> +}
> +
> +AVFilter avfilter_vf_drawtext = {
> +    .name          = "drawtext",
> +    .description   = NULL_IF_CONFIG_SMALL("Draw text on top of video frames using libfreetype library."),
> +    .priv_size     = sizeof(DrawTextContext),
> +    .init          = init,
> +    .uninit        = uninit,
> +    .query_formats = query_formats,
> +
> +    .inputs    = (AVFilterPad[]) {{ .name             = "default",
> +                                    .type             = AVMEDIA_TYPE_VIDEO,
> +                                    .get_video_buffer = avfilter_null_get_video_buffer,
> +                                    .start_frame      = avfilter_null_start_frame,
> +                                    .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}},
> +};
> -- 
> 1.7.2.3
>

-- 
M?ns Rullg?rd
mans at mansr.com



More information about the ffmpeg-devel mailing list