[FFmpeg-devel] [lavfi][aalib][PATCH] lavfi: Proposed changes to vf_drawtext filter to use new fontconfig utils.

Stefano Sabatini stefasab at gmail.com
Wed Jul 2 01:32:27 CEST 2014


On date Monday 2014-06-30 12:52:45 +0400, iamtakingiteasy at eientei.org encoded:
[...]
> From 61eb8f5813d5d1f1cd4b18b562830f16a24bfb54 Mon Sep 17 00:00:00 2001
> From: Alexander Tumin <iamtakingiteasy at eientei.org>
> Date: Sun, 29 Jun 2014 16:41:45 +0400

> Subject: [PATCH] lavfi: Generalization of freetype/fontconfig font loading.
>
> Introduced new freetypeutils.h/c helper for font loading and discovery
> for video filters.

nit++: lavfi: generalize freetype/fontconfig font loading

Introduce new freetypeutils.h/c helper for font loading and discovery
for video filters.

> 
> 
> lavfi: Proposed changes to vf_drawtext filter to use new fontconfig utils.
> ---
>  Changelog                   |   1 +
>  MAINTAINERS                 |   1 +
>  libavfilter/Makefile        |   1 +
>  libavfilter/freetypeutils.c | 175 ++++++++++++++++++++++++++++++++++++++++++++
>  libavfilter/freetypeutils.h | 109 +++++++++++++++++++++++++++
>  libavfilter/version.h       |   2 +-
>  libavfilter/vf_drawtext.c   | 147 ++++---------------------------------
>  7 files changed, 301 insertions(+), 135 deletions(-)
>  create mode 100644 libavfilter/freetypeutils.c
>  create mode 100644 libavfilter/freetypeutils.h
> 
> diff --git a/Changelog b/Changelog
> index 0346877..e51a93a 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -30,6 +30,7 @@ version <next>:
>  - zoompan filter
>  - signalstats filter
>  - hqx filter (hq2x, hq3x, hq4x)

> +- Freetypeutils helper for filters

This really belongs to the internal API, so this shouldn't be
advertised in the changelog.

>  
>  
>  version 2.2:
> diff --git a/MAINTAINERS b/MAINTAINERS
> index d06030b..5f09c9b 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -325,6 +325,7 @@ libavfilter
>  ===========
>  
>  Generic parts:
> +  freetypeutils.c                       Alexander Tumin
>    graphdump.c                           Nicolas George
>  
>  Filters:
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index 08817ee..a544ca6 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -122,6 +122,7 @@ OBJS-$(CONFIG_FORMAT_FILTER)                 += vf_format.o
>  OBJS-$(CONFIG_FRAMESTEP_FILTER)              += vf_framestep.o
>  OBJS-$(CONFIG_FPS_FILTER)                    += vf_fps.o
>  OBJS-$(CONFIG_FRAMEPACK_FILTER)              += vf_framepack.o
> +OBJS-$(CONFIG_LIBFREETYPE)                   += freetypeutils.o
>  OBJS-$(CONFIG_FREI0R_FILTER)                 += vf_frei0r.o
>  OBJS-$(CONFIG_GEQ_FILTER)                    += vf_geq.o
>  OBJS-$(CONFIG_GRADFUN_FILTER)                += vf_gradfun.o
> diff --git a/libavfilter/freetypeutils.c b/libavfilter/freetypeutils.c
> new file mode 100644
> index 0000000..c850b18
> --- /dev/null
> +++ b/libavfilter/freetypeutils.c
> @@ -0,0 +1,175 @@
> +/*
> + * 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
> + */
> +
> +#include "freetypeutils.h"
> +
> +int ff_freetype_init(FFFreetypeContext *ctx, void *avcl)
> +{
> +    int err;
> +
> +    if (!ctx || !avcl) {
> +        return AVERROR(EINVAL);
> +    }
> +
> +    ctx->avcl = avcl;
> +
> +    if ((err = FT_Init_FreeType(&ctx->ft_library))) {
> +        av_log(ctx->avcl, AV_LOG_ERROR, "Could not initialize FreeType2: %s\n", FT_ERRMSG(err));
> +        return AVERROR(ENOMEM);
> +    }
> +
> +
> +#if CONFIG_LIBFONTCONFIG
> +    ctx->fc_library = FcInitLoadConfigAndFonts();
> +    if (!ctx->fc_library) {
> +        av_log(ctx->avcl, AV_LOG_ERROR, "Could not initialize fontconfig\n");
> +        return AVERROR(ENOMEM);
> +    }
> +#else
> +    ctx->fc_library = NULL;
> +#endif
> +    return 0;
> +}
> +
> +static int ff_freetype_load_file(FFFreetypeContext *ctx, FT_Face *face, uint8_t *filepath, int index, int logfail)
> +{
> +    int err;
> +    if ((err = FT_New_Face(ctx->ft_library, filepath, index, face))) {
> +        if (logfail) {
> +            av_log(ctx->avcl, AV_LOG_ERROR, "Could not load font \"%s\": %s\n", filepath, FT_ERRMSG(err));
> +        }
> +        return AVERROR(EINVAL);
> +    }
> +
> +    av_log(ctx->avcl, AV_LOG_INFO, "Loaded font %s\n", filepath);
> +    return 0;
> +}
> +
> +#if CONFIG_LIBFONTCONFIG
> +static int ff_freetype_load_fontconfig(FFFreetypeContext *ctx, FT_Face *face, uint8_t *selector, int index)
> +{
> +    FcPattern *pattern = NULL, *bestmatch = NULL;
> +    FcResult res = FcResultMatch;
> +    unsigned char *filepath = 0;
> +    int err;
> +
> +    pattern = FcNameParse(selector);
> +
> +    if (!pattern) {
> +        av_log(ctx->avcl, AV_LOG_ERROR, "Could not parse fontconfig pattern\n");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    FcDefaultSubstitute(pattern);
> +
> +    if (!FcConfigSubstitute(ctx->fc_library, pattern, FcMatchPattern)) {
> +        av_log(ctx->avcl, AV_LOG_ERROR, "Could not perform config substitution for pattern\n");
> +        err = AVERROR(ENOMEM);
> +        goto fontconfig_cleanup;
> +    }
> +
> +    bestmatch = FcFontMatch(ctx->fc_library, pattern, &res);
> +
> +    if (!bestmatch || res != FcResultMatch) {
> +        av_log(ctx->avcl, AV_LOG_ERROR, "Could not find any matches for given pattern.\n");
> +        err = AVERROR(ENOENT);
> +        goto fontconfig_cleanup;
> +    }
> +
> +    if (index < 0) {
> +        if (FcPatternGetInteger(bestmatch, FC_INDEX, 0, &index) != FcResultMatch) {
> +            av_log(ctx->avcl, AV_LOG_ERROR, "Could not find font matching given pattern\n");
> +            err = AVERROR(EINVAL);
> +            goto fontconfig_cleanup;
> +        }
> +    }
> +    if (FcPatternGetString(bestmatch, FC_FILE, 0, &filepath) != FcResultMatch) {
> +        av_log(ctx->avcl, AV_LOG_ERROR, "Could not find font matching given pattern\n");
> +        err =AVERROR(EINVAL);
> +        goto fontconfig_cleanup;
> +    }
> +
> +    err = ff_freetype_load_file(ctx, face, filepath, index, 1);
> +
> +fontconfig_cleanup:
> +    if (pattern) {
> +        FcPatternDestroy(pattern);
> +    }
> +
> +    if (bestmatch) {
> +        FcPatternDestroy(bestmatch);
> +    }
> +
> +    return err;
> +}
> +#endif
> +
> +int ff_freetype_load(FFFreetypeContext *ctx, FT_Face *face, uint8_t *selector, int index)
> +{
> +    int err;
> +
> +    if (!ctx || !face || !selector) {
> +        return AVERROR(EINVAL);
> +    }
> +
> +    err = ff_freetype_load_file(ctx, face, selector, (index < 0) ? 0 : index, !CONFIG_LIBFONTCONFIG);
> +
> +    if (!err) {
> +        return 0;
> +    }
> +
> +#if CONFIG_LIBFONTCONFIG
> +    err = ff_freetype_load_fontconfig(ctx, face, selector, index);
> +#endif
> +
> +    return err;
> +}
> +
> +int ff_freetype_stroker_new(FFFreetypeContext *ctx, FT_Stroker *stroker)
> +{
> +    int err;
> +
> +    if (!ctx || !stroker) {
> +        return AVERROR(EINVAL);
> +    }
> +
> +    if ((err = FT_Stroker_New(ctx->ft_library, stroker))) {
> +        av_log(ctx->avcl, AV_LOG_ERROR, "Could not create new stroker: %s\n", FT_ERRMSG(err));
> +        return AVERROR_EXTERNAL;
> +    }
> +
> +    return 0;
> +}
> +
> +void ff_freetype_uninit(FFFreetypeContext *ctx)
> +{
> +    if (!ctx) {
> +        return;
> +    }
> +
> +    ctx->avcl = NULL;
> +
> +    FT_Done_FreeType(ctx->ft_library);
> +
> +#if CONFIG_LIBFONTCONFIG
> +    if (ctx->fc_library) {
> +        FcConfigDestroy(ctx->fc_library);
> +    }
> +    ctx->fc_library = NULL;
> +#endif
> +}
> diff --git a/libavfilter/freetypeutils.h b/libavfilter/freetypeutils.h
> new file mode 100644
> index 0000000..00dac0c
> --- /dev/null
> +++ b/libavfilter/freetypeutils.h
> @@ -0,0 +1,109 @@
> +/*
> + * 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
> + */
> +
> +#ifndef AVFILTER_FREETYPEUTILS_H
> +#define AVFILTER_FREETYPEUTILS_H
> +
> +/**
> + * @file
> + * FreeType2/Fontconfig font loading utilities
> + */
> +
> +#include "config.h"
> +
> +#include "internal.h"
> +
> +#include <ft2build.h>
> +#include FT_FREETYPE_H
> +#include FT_GLYPH_H
> +#include FT_STROKER_H
> +
> +#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
> +
> +#if CONFIG_LIBFONTCONFIG
> +#include <fontconfig/fontconfig.h>
> +#define FCTYPE FcConfig
> +#else
> +#define FCTYPE void
> +#endif
> +
> +typedef struct FFFreetypeContext {
> +    void       *avcl;       ///< A pointer to an arbitrary struct of which the first field is a pointer to an AVClass struct.
> +    FT_Library  ft_library; ///< FreeType library handle
> +    FCTYPE     *fc_library; ///< Fontconfig library handle
> +} FFFreetypeContext;
> +
> +/**
> + * Init a freetype context

Nit: missing ending point.

> + *
> + * If fontconfig selected at build system configuration time, additional fontconfig
> + * matcher is enabled.
> + *
> + * @param ctx  freetype context

> + * @param avcl logging context, a pointer to an arbitrary struct of which the first field is a pointer to an AVClass struct.
> + *
> + * @return 0 on success or < 0 on failure.

here you can remove the ending points

> + */
> +int ff_freetype_init(FFFreetypeContext *ctx, void *avcl);
> +
> +/**

> + * Load a font for given sellector, which can be either:

sellector typo

> + * - a filepath, or
> + * - a fontconfig pattern in FcNameUnparse format.
> + *   Note that FcNameUnparse function returns value, which suposed to be
> + *   free'd by you.

freed?

> + *
> + * @param ctx      font context
> + * @param face     font face to populate on successful font match.
> + *                 You will have to free it with FT_Done_Face afterwards.
> + * @param selector either filepath or a fontconfig pattern (if available)
> + * @param index    font index, use -1 to get default font
> + *

> + * @return 0 on success or < 0 on failure.

you can drop final dot

> + *
> + */
> +int ff_freetype_load(FFFreetypeContext *ctx, FT_Face *face, uint8_t *selector, int index);
> +
> +/**
> + * Allocate a new FT_Stroker using wrapped FT_Library.
> + *
> + * @param ctx     font context
> + * @param stroker stroker to allocate.
> + *                Yoy will have to free it with FT_Stroker_Done afterwards.

yoy

> + *
> + * @return 0 on success or < 0 on failure.

nit: ditto

> + */
> +int ff_freetype_stroker_new(FFFreetypeContext *ctx, FT_Stroker *stroker);
> +
> +/**
> + * Uninit a freetype context

missing final point, or you can skip the doxy altogether in this case.

> + */
> +void ff_freetype_uninit(FFFreetypeContext *ctx);
> +
> +#endif /* AVFILTER_FREETYPEUTILS_H */
> diff --git a/libavfilter/version.h b/libavfilter/version.h
> index f125032..bf9191e 100644
> --- a/libavfilter/version.h
> +++ b/libavfilter/version.h
> @@ -30,7 +30,7 @@
>  #include "libavutil/version.h"
>  
>  #define LIBAVFILTER_VERSION_MAJOR   4
> -#define LIBAVFILTER_VERSION_MINOR   9
> +#define LIBAVFILTER_VERSION_MINOR  10
>  #define LIBAVFILTER_VERSION_MICRO 100
>  
>  #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
> diff --git a/libavfilter/vf_drawtext.c b/libavfilter/vf_drawtext.c
> index adc587e..a973b19 100644
> --- a/libavfilter/vf_drawtext.c
> +++ b/libavfilter/vf_drawtext.c
> @@ -36,10 +36,6 @@
>  #include <time.h>
>  #include <unistd.h>
>  
> -#if CONFIG_LIBFONTCONFIG
> -#include <fontconfig/fontconfig.h>
> -#endif
> -
>  #include "libavutil/avstring.h"
>  #include "libavutil/bprint.h"
>  #include "libavutil/common.h"
> @@ -54,14 +50,10 @@
>  #include "avfilter.h"
>  #include "drawutils.h"
>  #include "formats.h"
> +#include "freetypeutils.h"
>  #include "internal.h"
>  #include "video.h"
>  
> -#include <ft2build.h>
> -#include FT_FREETYPE_H
> -#include FT_GLYPH_H
> -#include FT_STROKER_H
> -
>  static const char *const var_names[] = {
>      "dar",
>      "hsub", "vsub",
> @@ -131,7 +123,7 @@ typedef struct DrawTextContext {
>      enum expansion_mode exp_mode;   ///< expansion mode to use for the text
>      int reinit;                     ///< tells if the filter is being reinited
>  #if CONFIG_LIBFONTCONFIG
> -    uint8_t *font;              ///< font to be used
> +    uint8_t *font;                  ///< font to be used
>  #endif
>      uint8_t *fontfile;              ///< font to be used
>      uint8_t *text;                  ///< text to be drawn
> @@ -159,7 +151,7 @@ typedef struct DrawTextContext {
>      FFDrawColor bordercolor;        ///< border color
>      FFDrawColor boxcolor;           ///< background color
>  
> -    FT_Library library;             ///< freetype font library handle
> +    FFFreetypeContext font_ctx;     ///< freetype font library handle

freetype_ctx or ft_ctx looks more descriptive

>      FT_Face face;                   ///< freetype font face handle
>      FT_Stroker stroker;             ///< freetype stroker handle
>      struct AVTreeNode *glyphs;      ///< rendered glyphs, stored using the UTF-32 char code
> @@ -246,20 +238,6 @@ static const AVOption drawtext_options[]= {
>  
>  AVFILTER_DEFINE_CLASS(drawtext);
>  
> -#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
> -
>  typedef struct Glyph {
>      FT_Glyph *glyph;
>      uint32_t code;
> @@ -348,110 +326,6 @@ error:
>      return ret;
>  }
>  
> -static int load_font_file(AVFilterContext *ctx, const char *path, int index)
> -{
> -    DrawTextContext *s = ctx->priv;
> -    int err;
> -
> -    err = FT_New_Face(s->library, path, index, &s->face);
> -    if (err) {
> -        av_log(ctx, AV_LOG_ERROR, "Could not load font \"%s\": %s\n",
> -               s->fontfile, FT_ERRMSG(err));
> -        return AVERROR(EINVAL);
> -    }
> -    return 0;
> -}
> -
> -#if CONFIG_LIBFONTCONFIG
> -static int load_font_fontconfig(AVFilterContext *ctx)
> -{
> -    DrawTextContext *s = ctx->priv;
> -    FcConfig *fontconfig;
> -    FcPattern *pat, *best;
> -    FcResult result = FcResultMatch;
> -    FcChar8 *filename;
> -    int index;
> -    double size;
> -    int err = AVERROR(ENOENT);
> -
> -    fontconfig = FcInitLoadConfigAndFonts();
> -    if (!fontconfig) {
> -        av_log(ctx, AV_LOG_ERROR, "impossible to init fontconfig\n");
> -        return AVERROR_UNKNOWN;
> -    }
> -    pat = FcNameParse(s->fontfile ? s->fontfile :
> -                          (uint8_t *)(intptr_t)"default");
> -    if (!pat) {
> -        av_log(ctx, AV_LOG_ERROR, "could not parse fontconfig pat");
> -        return AVERROR(EINVAL);
> -    }
> -
> -    FcPatternAddString(pat, FC_FAMILY, s->font);
> -    if (s->fontsize)
> -        FcPatternAddDouble(pat, FC_SIZE, (double)s->fontsize);
> -
> -    FcDefaultSubstitute(pat);
> -
> -    if (!FcConfigSubstitute(fontconfig, pat, FcMatchPattern)) {
> -        av_log(ctx, AV_LOG_ERROR, "could not substitue fontconfig options"); /* very unlikely */
> -        FcPatternDestroy(pat);
> -        return AVERROR(ENOMEM);
> -    }
> -
> -    best = FcFontMatch(fontconfig, pat, &result);
> -    FcPatternDestroy(pat);
> -
> -    if (!best || result != FcResultMatch) {
> -        av_log(ctx, AV_LOG_ERROR,
> -               "Cannot find a valid font for the family %s\n",
> -               s->font);
> -        goto fail;
> -    }
> -
> -    if (
> -        FcPatternGetInteger(best, FC_INDEX, 0, &index   ) != FcResultMatch ||
> -        FcPatternGetDouble (best, FC_SIZE,  0, &size    ) != FcResultMatch) {
> -        av_log(ctx, AV_LOG_ERROR, "impossible to find font information");
> -        return AVERROR(EINVAL);
> -    }
> -
> -    if (FcPatternGetString(best, FC_FILE, 0, &filename) != FcResultMatch) {
> -        av_log(ctx, AV_LOG_ERROR, "No file path for %s\n",
> -               s->font);
> -        goto fail;
> -    }
> -
> -    av_log(ctx, AV_LOG_INFO, "Using \"%s\"\n", filename);
> -    if (!s->fontsize)
> -        s->fontsize = size + 0.5;
> -
> -    err = load_font_file(ctx, filename, index);
> -    if (err)
> -        return err;
> -    FcConfigDestroy(fontconfig);
> -fail:
> -    FcPatternDestroy(best);
> -    return err;
> -}
> -#endif
> -
> -static int load_font(AVFilterContext *ctx)
> -{
> -    DrawTextContext *s = ctx->priv;
> -    int err;
> -
> -    /* load the face, and set up the encoding, which is by default UTF-8 */
> -    err = load_font_file(ctx, s->fontfile, 0);
> -    if (!err)
> -        return 0;
> -#if CONFIG_LIBFONTCONFIG
> -    err = load_font_fontconfig(ctx);
> -    if (!err)
> -        return 0;
> -#endif
> -    return err;
> -}
> -
>  static int load_textfile(AVFilterContext *ctx)
>  {
>      DrawTextContext *s = ctx->priv;
> @@ -526,13 +400,18 @@ static av_cold int init(AVFilterContext *ctx)
>          return AVERROR(EINVAL);
>      }
>  
> -    if ((err = FT_Init_FreeType(&(s->library)))) {

> +    if (ff_freetype_init(&s->font_ctx, ctx)) {
>          av_log(ctx, AV_LOG_ERROR,
> -               "Could not load FreeType: %s\n", FT_ERRMSG(err));
> +               "Could not load FreeType\n");
>          return AVERROR(EINVAL);

    if ((ret = ff_freetype_init(&s->font_ctx, ctx)) < 0)
       return ret;

???

Assuming the log is redundant in this case.


>  
> -    err = load_font(ctx);
> +#if CONFIG_LIBFONTCONFIG
> +    err = ff_freetype_load(&s->font_ctx, &s->face, (s->fontfile == 0) ? s->font : s->fontfile, -1);
> +#else
> +    err = ff_freetype_load(&s->font_ctx, &s->face, s->fontfile, -1);
> +#endif
> +
>      if (err)
>          return err;
>      if (!s->fontsize)
> @@ -544,7 +423,7 @@ static av_cold int init(AVFilterContext *ctx)
>      }
>  
>      if (s->borderw) {
> -        if (FT_Stroker_New(s->library, &s->stroker)) {

> +        if (ff_freetype_stroker_new(&s->font_ctx, &s->stroker)) {
>              av_log(ctx, AV_LOG_ERROR, "Coult not init FT stroker\n");
>              return AVERROR_EXTERNAL;

If you're using an internal function you should return its error code.
Also you could probably create the stroker when initing the context.

[...]
-- 
FFmpeg = Faithless and Funny Majestic Proud Extended Gigant


More information about the ffmpeg-devel mailing list