[FFmpeg-devel] aalib video filter patch review request
Stefano Sabatini
stefasab at gmail.com
Fri Jun 27 12:37:15 CEST 2014
On date Monday 2014-06-23 20:57:42 +0400, iamtakingiteasy at eientei.org encoded:
> Hello, i have written (basing on drawtext) a simple video filter,
> which utilizes aalib to render input frames as ascii-art and
> rastarizes this ascii-art to output frames using freetype2 library.
> Also fontconfig can be used for font discovery.
>
> Attaching a patch implementing this filter.
>
> Here is example video:
>
> before:
> http://www.youtube.com/watch?v=44oBi1ikNZk
>
> and after applying this filter:
>
> ffmpeg -i disharmony_plain.mp4 -vcodec libx264 -preset slow -crf 18
> -pix_fmt yuv420p -vf 'scale=iw/3:ih/4,
> aa=fontname=terminus:fontsize=12:linespacing=1.0:contrast=50,
> pad=width=1920:x=(ow-iw)/2' -aspect 16:9 -acodec aac -strict -2 -f
> mp4 disharmony_aa_filter.mp4
>
> http://www.youtube.com/watch?v=LQytXqovLOM
>
> I hope for suggestions how to adopt this code in order to upstream it.
>
> This is one of my first steps on FOSS world and the first one
> towards ffmpeg suite, so it most likely will be needing a strong
> scepsis during review, because approaches i took most likely wasn't
> perfect ones to say the least.
>
> Thanks.
> From 1391af4f776b4834cb5d803247cbf858faf13af3 Mon Sep 17 00:00:00 2001
> From: Alexander Tumin <iamtakingiteasy at eientei.org>
> Date: Mon, 23 Jun 2014 13:03:44 +0400
> Subject: [PATCH] added aalib video filter
Nit: lavfi: add aalib video filter
>
> aalib is a library which produces ascii-art representation of iamge pixels
iamge typo
>
> Signed-off-by: Alexander Tumin <iamtakingiteasy at eientei.org>
> ---
> Changelog | 1 +
> configure | 5 +
> libavfilter/Makefile | 1 +
> libavfilter/allfilters.c | 1 +
> libavfilter/version.h | 2 +-
> libavfilter/vf_aa.c | 590 +++++++++++++++++++++++++++++++++++++++++++++++
missing doc/filters.texi notes
> 6 files changed, 599 insertions(+), 1 deletion(-)
> create mode 100644 libavfilter/vf_aa.c
>
> diff --git a/Changelog b/Changelog
> index 0346877..cc1f937 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -30,6 +30,7 @@ version <next>:
> - zoompan filter
> - signalstats filter
> - hqx filter (hq2x, hq3x, hq4x)
> +- aalib video filter
>
>
> version 2.2:
> diff --git a/configure b/configure
> index ddf0f3c..1e9e338 100755
> --- a/configure
> +++ b/configure
> @@ -196,6 +196,7 @@ External library support:
> --enable-gnutls enable gnutls [no]
> --disable-iconv disable iconv [autodetect]
> --enable-ladspa enable LADSPA audio filtering
> + --enable-libaa enable libaa [no]
> --enable-libaacplus enable AAC+ encoding via libaacplus [no]
> --enable-libass enable libass subtitles rendering [no]
> --enable-libbluray enable BluRay reading using libbluray [no]
> @@ -1318,6 +1319,7 @@ EXTERNAL_LIBRARY_LIST="
> gnutls
> iconv
> ladspa
> + libaa
> libaacplus
> libass
> libbluray
> @@ -2484,6 +2486,7 @@ unix_protocol_deps="sys_un_h"
> unix_protocol_select="network"
>
> # filters
> +aa_filter_deps="libfreetype libaa"
> aconvert_filter_deps="swresample"
> amovie_filter_deps="avcodec avformat"
> aresample_filter_deps="swresample"
> @@ -4689,6 +4692,8 @@ enabled decklink && { check_header DeckLinkAPI.h || die "ERROR: DeckLin
> enabled frei0r && { check_header frei0r.h || die "ERROR: frei0r.h header not found"; }
> enabled gnutls && require_pkg_config gnutls gnutls/gnutls.h gnutls_global_init
> enabled ladspa && { check_header ladspa.h || die "ERROR: ladspa.h header not found"; }
> +enabled libaa && { check_lib aalib.h aa_init -laa ||
> + die "ERROR: libaa (aalib) must be installed."; }
> enabled libiec61883 && require libiec61883 libiec61883/iec61883.h iec61883_cmp_connect -lraw1394 -lavc1394 -lrom1394 -liec61883
> enabled libaacplus && require "libaacplus >= 2.0.0" aacplus.h aacplusEncOpen -laacplus
> enabled libass && require_pkg_config libass ass/ass.h ass_library_init
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index 08817ee..c8016e3 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -88,6 +88,7 @@ OBJS-$(CONFIG_SINE_FILTER) += asrc_sine.o
>
> OBJS-$(CONFIG_ANULLSINK_FILTER) += asink_anullsink.o
>
> +OBJS-$(CONFIG_AA_FILTER) += vf_aa.o
> OBJS-$(CONFIG_ASS_FILTER) += vf_subtitles.o
> OBJS-$(CONFIG_ALPHAEXTRACT_FILTER) += vf_extractplanes.o
> OBJS-$(CONFIG_ALPHAMERGE_FILTER) += vf_alphamerge.o
> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> index e4ac983..dac7842 100644
> --- a/libavfilter/allfilters.c
> +++ b/libavfilter/allfilters.c
> @@ -106,6 +106,7 @@ void avfilter_register_all(void)
>
> REGISTER_FILTER(ANULLSINK, anullsink, asink);
>
> + REGISTER_FILTER(AA, aa, vf);
> REGISTER_FILTER(ALPHAEXTRACT, alphaextract, vf);
> REGISTER_FILTER(ALPHAMERGE, alphamerge, vf);
> REGISTER_FILTER(ASS, ass, vf);
> 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_aa.c b/libavfilter/vf_aa.c
> new file mode 100644
> index 0000000..b9957a5
> --- /dev/null
> +++ b/libavfilter/vf_aa.c
> @@ -0,0 +1,590 @@
> +/*
> + * Copyright (c) 2014 Alexander Tumin <iamtakingiteasy at eientei.org>
> + *
> + * 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
> + * aa (ascii-art) video filter using aalib
> + *
> + * Each 4 pixels are related to a single ascii character
> + *
> + * filter by Alexander Tumin
Please keep your name off from this notice, git is good enough for
tracking authorship.
> + */
> +
> +
> +#include "config.h"
> +
> +#include <float.h> // DBL_MIN, DBL_MAX
> +
> +#include "libavutil/opt.h"
> +#include "libavutil/tree.h"
> +#include "avfilter.h"
> +#include "drawutils.h"
> +#include "internal.h"
> +#include "formats.h"
> +#include "video.h"
> +
> +#include <aalib.h>
> +
> +#if CONFIG_LIBFONTCONFIG
> +#include <fontconfig/fontconfig.h>
> +#endif
> +
> +#include <ft2build.h>
> +#include FT_FREETYPE_H
> +#include FT_GLYPH_H
> +#include FT_STROKER_H
> +
> +typedef struct AAContext {
> + const AVClass *class;
> +#if CONFIG_LIBFONTCONFIG
> + uint8_t *fontname; ///< font family name (Sans, Serif, etc)
> + uint8_t *fontstyle; ///< font style (bold, italic, etc)
> +#endif
> + uint8_t *fontfile; ///< font file
> + double fontsize; ///< font size in pixels
> + double linespacing; ///< font vertical line spacing
> +
> + FFDrawContext dc;
> + FFDrawColor fgcolor; ///< font color
> + FFDrawColor bgcolor; ///< background color
> +
> + FT_Library library; ///< freetype library
> + FT_Face face; ///< freetype font face
> + struct AVTreeNode *glyphs; ///< rendered glyphs
> + int xadvance; ///< glyph x advance
> + int yadvance; ///< glyph y advance
> +
> + aa_context *aa; ///< aalib context
> + struct aa_hardware_params aa_params;
> + struct aa_renderparams renderparams;
> + AVFrame *curframe; ///< current rendering frame
> + int x; ///< cursor x (in characters)
> + int y; ///< corsor y (int characters)
> + int w; ///< canvas size (in characters)
> + int h;
> +
> + int brightness; ///< aalib brightness modifier 0..255
> + int contrast; ///< aalib contrast modifier 0..127
> + double gamma; ///< aalib gamma modifier
> + int inversion; ///< whether to use aalib inversion
> + int aaflags;
> +} AAContext;
> +
> +typedef struct Glyph {
> + FT_Glyph *glyph; ///< freetype glyph
> + uint32_t code; ///< glyph codepoint
> + FT_Bitmap bitmap; ///< glyph bitmap
> + FT_BBox bbox; ///< glyph bounding box
> + int bitmap_left; ///< distance from origin to left boundary
> + int bitmap_top; ///< distance from origin to top boundary
> +} Glyph;
> +
> +static int glyph_cmp(void *key, const void *g) {
> + const Glyph *a = key;
> + const Glyph *b = g;
> + int64_t diff = (int64_t)a->code - (int64_t)b->code;
> + return diff > 0 ? 1 : diff < 0 ? -1 : 0;
> +}
> +
> +static int vf_driver_init(const struct aa_hardware_params *source, const void *data, struct aa_hardware_params *dest, void **params)
> +{
> + *dest = *source;
> + return 1;
> +}
> +
> +static void vf_driver_uninit(struct aa_context *context)
> +{
> +}
> +
> +static void vf_driver_setattr(aa_context *context, int attr)
> +{
> +
> +}
> +
> +static void vf_driver_print(aa_context *context, const char *text)
> +{
> + AAContext *s = context->driverdata;
> + const unsigned char *c = text;
> + Glyph *glyph = NULL;
> + int cx, cy;
> + if (s) {
> + while(*c) {
> + Glyph dummy = { 0 };
> + dummy.code = *c;
> +
> + glyph = av_tree_find(s->glyphs, &dummy, glyph_cmp, NULL);
> +
> + if (!glyph) {
> + s->x += 1;
> + c++;
> + continue;
> + }
> +
> + cx = (s->x * s->xadvance) + glyph->bitmap_left;
> + cy = (s->y * s->yadvance) - glyph->bitmap_top;
> +
> + ff_blend_mask(&s->dc, &s->fgcolor,
> + s->curframe->data,
> + s->curframe->linesize,
> + s->curframe->width, s->curframe->height,
> + glyph->bitmap.buffer, glyph->bitmap.pitch,
> + glyph->bitmap.width, glyph->bitmap.rows,
> + glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO ? 0 : 3,
> + 0, cx, cy);
> +
> + s->x += 1;
> + c++;
> + }
> + }
> +}
> +
> +static void vf_driver_getsize(aa_context *context, int *width, int *height)
> +{
> + AAContext *s = context->driverdata;
> + if (s) {
> + *width = s->w;
> + *height = s->h;
> + }
> +}
> +
> +static void vf_driver_gotoxy(aa_context *context, int x, int y)
> +{
> + AAContext *s = context->driverdata;
> + if (s) {
> + s->x = x;
> + s->y = y;
> + }
> +}
> +
> +static struct aa_driver vf_driver = {
> + .shortname = "vf",
> + .name = "video filter driver",
> + .init = vf_driver_init,
> + .uninit = vf_driver_uninit,
> + .setattr = vf_driver_setattr,
> + .getsize = vf_driver_getsize,
> + .print = vf_driver_print,
> + .gotoxy = vf_driver_gotoxy
> +};
what's this good for?
> +
> +#define OFFSET(x) offsetof(AAContext, x)
> +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
> +
> +static const AVOption aa_options[] = {
> + {"fontfile", "set font file", OFFSET(fontfile), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS},
> +#if CONFIG_LIBFONTCONFIG
> + {"fontname", "set font name", OFFSET(fontname), AV_OPT_TYPE_STRING, {.str="Monospace"}, CHAR_MIN, CHAR_MAX, FLAGS},
> + {"fontstyle", "set font style", OFFSET(fontstyle), AV_OPT_TYPE_STRING, {.str="Regular"}, CHAR_MIN, CHAR_MAX, FLAGS},
> +#endif
> + {"fgcolor", "set foreground color", OFFSET(fgcolor.rgba), AV_OPT_TYPE_COLOR, {.str="white"}, CHAR_MIN, CHAR_MAX, FLAGS},
> + {"bgcolor", "set background color", OFFSET(bgcolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, CHAR_MIN, CHAR_MAX, FLAGS},
> + {"fontsize", "set font size", OFFSET(fontsize), AV_OPT_TYPE_DOUBLE, {.dbl=0.0}, 0, DBL_MAX, FLAGS},
> + {"linespacing", "set line spacing", OFFSET(linespacing), AV_OPT_TYPE_DOUBLE, {.dbl=1.2}, 0, DBL_MAX, FLAGS},
> + {"brightness", "set brightness", OFFSET(brightness), AV_OPT_TYPE_INT, {.i64=0}, 0, 255, FLAGS},
> + {"contrast", "set contrast", OFFSET(contrast), AV_OPT_TYPE_INT, {.i64=0}, 0, 127, FLAGS},
> + {"gamma", "set gamma", OFFSET(gamma), AV_OPT_TYPE_DOUBLE, {.dbl=1.0}, DBL_MIN, DBL_MAX, FLAGS},
> + {"inversion", "set inversion", OFFSET(inversion), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS},
> + {"aaflags", "aalib flags", OFFSET(aaflags), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, INT_MAX, FLAGS, "aaflags"},
> + {"reverse", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AA_REVERSE_MASK}, .flags = FLAGS, .unit = "aaflags"},
> + {"all", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AA_ALL}, .flags = FLAGS, .unit = "aaflags"},
> + {"eight", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AA_EIGHT}, .flags = FLAGS, .unit = "aaflags"},
> + {"extended", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AA_EXTENDED}, .flags = FLAGS, .unit = "aaflags"},
> + {NULL}
> +};
> +
> +AVFILTER_DEFINE_CLASS(aa);
> +
> +#undef __FTERRORS_H__
> +
> +#define FT_ERROR_START_LIST {
> +#define FT_ERRORDEF(e, v, s) { (e), (s) },
> +#define FT_ERROR_END_LIST { 0, NULL } };
> +
> +const struct {
> + int err_code;
> + const char *err_msg;
> +} ft_errors[] =
> +#include FT_ERRORS_H
> +
> +#define FT_ERRMSG(e) ft_errors[e].err_msg
> +
> +static int load_font_file(AVFilterContext *ctx, const char *path, int index, int faillog) {
> + int err;
> + AAContext *s = ctx->priv;
> +
> + err = FT_New_Face(s->library, path, index, &s->face);
> + if (err) {
> + if (faillog) {
> + 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)
> +{
> + int err;
> + AAContext *s = ctx->priv;
> + FcConfig *fontconfig;
> + FcPattern *pat, *best;
> + FcResult result = FcResultMatch;
> + FcChar8 *filename;
> + int index;
> + double size;
> +
> + fontconfig = FcInitLoadConfigAndFonts();
> + if (!fontconfig) {
> + av_log(ctx, AV_LOG_ERROR, "Impossible to init fontconfig\n");
> + return AVERROR_UNKNOWN;
> + }
> +
> + pat = FcNameParse("default");
> + if (!pat) {
> + av_log(ctx, AV_LOG_ERROR, "Could not parse fontconfig pattern");
> + err = AVERROR(EINVAL);
> + goto fc_cleanup;
> + }
> +
> + FcPatternAddString(pat, FC_FAMILY, s->fontname);
> + FcPatternAddString(pat, FC_STYLE, s->fontstyle);
> + if (s->fontsize != 0.0) {
> + FcPatternAddDouble(pat, FC_SIZE, s->fontsize);
> + }
> +
> + FcDefaultSubstitute(pat);
> +
> + if (!FcConfigSubstitute(fontconfig, pat, FcMatchPattern)) {
> + av_log(ctx, AV_LOG_ERROR, "Could not substitute fontconfig options");
> + err = AVERROR(ENOMEM);
> + goto fc_cleanup;
> + }
> +
> + best = FcFontMatch(fontconfig, pat, &result);
> +
> + if (!best || result != FcResultMatch) {
> + av_log(ctx, AV_LOG_ERROR, "Cannot find a valid font for the family %s\n", s->fontname);
> + err = AVERROR(ENOENT);
> + goto pat_cleanup;
> + }
> +
> + 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");
> + err = AVERROR(EINVAL);
> + goto pat_cleanup;
> + }
> +
> + if (FcPatternGetString(best, FC_FILE, 0, &filename) != FcResultMatch) {
> + av_log(ctx, AV_LOG_ERROR, "No file path for %s\n", s->fontname);
> + err = AVERROR(ENOENT);
> + goto pat_cleanup;
> + }
> +
> + av_log(ctx, AV_LOG_INFO, "Using \"%s\"\n", filename);
> + if (s->fontsize == 0.0) {
> + s->fontsize = size;
> + }
> +
> + err = load_font_file(ctx, filename, index, 1);
> +
> +pat_cleanup:
> + FcPatternDestroy(pat);
> + FcPatternDestroy(best);
> +fc_cleanup:
> + FcConfigDestroy(fontconfig);
> + return err;
> +}
> +#endif
> +
> +static int load_font(AVFilterContext *ctx)
> +{
> + int err;
> + AAContext *s = ctx->priv;
> +#if CONFIG_LIBFONTCONFIG
> + int fail = 0;
> +#else
> + int fail = 1;
> +#endif
> +
> + err = load_font_file(ctx, s->fontfile, 0, fail);
> +
> + if (!err) {
> + return 0;
> + }
> +
> +#if CONFIG_LIBFONTCONFIG
> + err = load_font_fontconfig(ctx);
> + if (!err) {
> + return 0;
> + }
> +#endif
> +
> + return err;
> +}
> +
> +static int load_glyph(AVFilterContext *ctx, uint32_t code, Glyph **glyph_ptr)
> +{
> + int err;
> + AAContext *s = ctx->priv;
> + FT_BitmapGlyph bitmapglyph;
> + Glyph *glyph;
> + struct AVTreeNode *node = NULL;
> +
> + if (FT_Load_Char(s->face, code, FT_LOAD_DEFAULT)) {
> + return AVERROR(EINVAL);
> + }
> +
> + if (!(glyph = av_mallocz(sizeof(Glyph))) || !(glyph->glyph = av_mallocz(sizeof(FT_Glyph)))) {
> + err = AVERROR(ENOMEM);
> + goto glyph_cleanup;
> + }
> + glyph->code = code;
> +
> + if (FT_Get_Glyph(s->face->glyph, glyph->glyph)) {
> + err = AVERROR(EINVAL);
> + goto glyph_cleanup;
> + }
> +
> + if (FT_Glyph_To_Bitmap(glyph->glyph, FT_RENDER_MODE_NORMAL, 0, 1)) {
> + err = AVERROR_EXTERNAL;
> + goto glyph_cleanup;
> + }
> +
> + bitmapglyph = (FT_BitmapGlyph) *glyph->glyph;
> + glyph->bitmap = bitmapglyph->bitmap;
> + glyph->bitmap_left = bitmapglyph->left;
> + glyph->bitmap_top = bitmapglyph->top;
> +
> + FT_Glyph_Get_CBox(*glyph->glyph, ft_glyph_bbox_pixels, &glyph->bbox);
> +
> + if (!(node = av_tree_node_alloc())) {
> + err = AVERROR(ENOMEM);
> + goto glyph_cleanup;
> + }
> +
> + av_tree_insert(&s->glyphs, glyph, glyph_cmp, &node);
> +
> + if (glyph_ptr) {
> + *glyph_ptr = glyph;
> + }
> +
> + return 0;
> +
> +glyph_cleanup:
> + if (glyph)
> + av_freep(&glyph->glyph);
> + av_freep(&glyph);
> + av_freep(&node);
> + return err;
> +}
I see that all this code is copied from vf_drawtext.c. This is not
good for maintainance since we don't want to maintain two exact
copies. You can follow two approaches, you move the common code to a
separate module (e.g. freetypeutils.c/h) or you mode the code of your
filter to vf_drawtext.c.
> +
> +static av_cold int init(AVFilterContext *ctx)
> +{
> + int err;
> + int i;
> + AAContext *s = ctx->priv;
> + Glyph *glyph;
> +
> + if (!s->fontfile && !CONFIG_LIBFONTCONFIG) {
> + av_log(ctx, AV_LOG_ERROR, "No font filename provided\n");
> + return AVERROR(EINVAL);
> + }
> +
> + err = FT_Init_FreeType(&s->library);
> +
> + if (err) {
> + av_log(ctx, AV_LOG_ERROR, "Could not load FreeType: %s\n", FT_ERRMSG(err));
> + return AVERROR(EINVAL);
> + }
> +
> + err = load_font(ctx);
> +
> + if (err) {
> + return err;
> + }
> +
> + if (s->fontsize == 0.0) {
> + s->fontsize = 10.0;
> + }
> +
> + err = FT_Set_Pixel_Sizes(s->face, 0, s->fontsize);
> +
> + if (err) {
> + av_log(ctx, AV_LOG_ERROR, "Could not set font size to %f pixels: %s\n", s->fontsize, FT_ERRMSG(err));
> + return AVERROR(EINVAL);
> + }
> +
> + for (i = 0; i < 256; i++) {
> + err = load_glyph(ctx, i, &glyph);
> + if (!err) {
> + s->xadvance = FFMAX(s->xadvance, glyph->bbox.xMax);
> + }
> + }
> +
> + s->yadvance = s->fontsize * s->linespacing;
> +
> + return 0;
> +}
> +
> +static int glyph_enu_free(void *key, void *value)
> +{
> + Glyph *glyph = value;
> +
> + FT_Done_Glyph(*glyph->glyph);
> + av_freep(&glyph->glyph);
> + av_free(value);
> + return 0;
> +}
> +
> +static av_cold void uninit(AVFilterContext *ctx)
> +{
> + AAContext *s = ctx->priv;
> + s->aa->driverdata = 0;
> + aa_close(s->aa);
> +
> + av_tree_enumerate(s->glyphs, NULL, NULL, glyph_enu_free);
> + av_tree_destroy(s->glyphs);
> + s->glyphs = NULL;
> +
> + FT_Done_Face(s->face);
> + FT_Done_FreeType(s->library);
> +}
> +
> +static int query_formats(AVFilterContext *ctx)
> +{
> + ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0));
> + return 0;
> +}
> +
> +static int filter_frame(AVFilterLink *inlink, AVFrame *in)
> +{
> + AVFilterContext *ctx = inlink->dst;
> + AVFilterLink *outlink = ctx->outputs[0];
> + AAContext *s = ctx->priv;
> + AVFrame *out;
> + uint8_t *framebuffer;
> + int y;
> +
> + out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
> + if (!out) {
> + av_frame_free(&in);
> + return AVERROR(ENOMEM);
> + }
> +
> + av_frame_copy_props(out, in);
> + out->width = outlink->w;
> + out->height = outlink->h;
> + ff_fill_rectangle(&s->dc, &s->bgcolor, out->data, out->linesize, 0, 0, out->width, out->height);
> +
> + framebuffer = aa_image(s->aa);
> +
> + for (y = 0; y < s->h * 2; y++) {
why "* 2" ?
> + memcpy(framebuffer + y * aa_imgwidth(s->aa),
> + in->data[0] + y * in->linesize[0],
> + FFMIN(in->linesize[0],aa_imgwidth(s->aa)));
probably you can replace this with aa_imgwidth(s->aa) since it looks
like aa_imgwidth() doesn't contain padding.
[...]
--
FFmpeg = Fostering Formidable Miracolous Prodigious Eccentric God
More information about the ffmpeg-devel
mailing list