[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, <ime));
> + 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