Revision a5b64584

View differences:

configure
169 169
                           and libraw1394 [no]
170 170
  --enable-libdirac        enable Dirac support via libdirac [no]
171 171
  --enable-libfaac         enable FAAC support via libfaac [no]
172
  --enable-libfreetype     enable libfreetype [no]
172 173
  --enable-libgsm          enable GSM support via libgsm [no]
173 174
  --enable-libmp3lame      enable MP3 encoding via libmp3lame [no]
174 175
  --enable-libnut          enable NUT (de)muxing via libnut,
......
928 929
    libdc1394
929 930
    libdirac
930 931
    libfaac
932
    libfreetype
931 933
    libgsm
932 934
    libmp3lame
933 935
    libnut
......
1077 1079
    llrintf
1078 1080
    local_aligned_16
1079 1081
    local_aligned_8
1082
    localtime_r
1080 1083
    log2
1081 1084
    log2f
1082 1085
    loongson
......
1462 1465
# filters
1463 1466
blackframe_filter_deps="gpl"
1464 1467
cropdetect_filter_deps="gpl"
1468
drawtext_filter_deps="libfreetype"
1465 1469
frei0r_filter_deps="frei0r dlopen strtok_r"
1466 1470
frei0r_src_filter_deps="frei0r dlopen strtok_r"
1467 1471
hqdn3d_filter_deps="gpl"
......
2782 2786
check_struct "sys/time.h sys/resource.h" "struct rusage" ru_maxrss
2783 2787
check_func  inet_aton $network_extralibs
2784 2788
check_func  isatty
2789
check_func  localtime_r
2785 2790
check_func  ${malloc_prefix}memalign            && enable memalign
2786 2791
check_func  mkstemp
2787 2792
check_func  mmap
......
2868 2873
    "libdirac_decoder/dirac_parser.h libdirac_encoder/dirac_encoder.h"  \
2869 2874
    "dirac_decoder_init dirac_encoder_init"
2870 2875
enabled libfaac    && require2 libfaac "stdint.h faac.h" faacEncGetVersion -lfaac
2876
enabled libfreetype && require_pkg_config freetype2 "ft2build.h freetype/freetype.h" FT_Init_FreeType
2871 2877
enabled libgsm     && require  libgsm gsm/gsm.h gsm_create -lgsm
2872 2878
enabled libmp3lame && require  "libmp3lame >= 3.98.3" lame/lame.h lame_set_VBR_quality -lmp3lame
2873 2879
enabled libnut     && require  libnut libnut.h nut_demuxer_init -lnut
doc/filters.texi
353 353
drawbox=10:20:200:60:red@@0.5"
354 354
@end example
355 355

  
356
@section drawtext
357

  
358
Draw text string or text from specified file on top of video using the
359
libfreetype library.
360

  
361
To enable compilation of this filter you need to configure FFmpeg with
362
@code{--enable-libfreetype}.
363

  
364
The filter also recognizes strftime() sequences in the provided text
365
and expands them accordingly. Check the documentation of strftime().
366

  
367
The filter accepts parameters as a list of @var{key}=@var{value} pairs,
368
separated by ":".
369

  
370
The description of the accepted parameters follows.
371

  
372
@table @option
373

  
374
@item fontfile
375
The font file to be used for drawing text. Path must be included.
376
This parameter is mandatory.
377

  
378
@item text
379
The text string to be drawn. The text must be a sequence of UTF-8
380
encoded characters.
381
This parameter is mandatory if no file is specified with the parameter
382
@var{textfile}.
383

  
384
@item textfile
385
A text file containing text to be drawn. The text must be a sequence
386
of UTF-8 encoded characters.
387

  
388
This parameter is mandatory if no text string is specified with the
389
parameter @var{text}.
390

  
391
If both text and textfile are specified, an error is thrown.
392

  
393
@item x, y
394
The offsets where text will be drawn within the video frame.
395
Relative to the top/left border of the output image.
396

  
397
The default value of @var{x} and @var{y} is 0.
398

  
399
@item fontsize
400
The font size to be used for drawing text.
401
The default value of @var{fontsize} is 16.
402

  
403
@item fontcolor
404
The color to be used for drawing fonts.
405
Either a string (e.g. "red") or in 0xRRGGBB[AA] format
406
(e.g. "0xff000033"), possibly followed by an alpha specifier.
407
The default value of @var{fontcolor} is "black".
408

  
409
@item boxcolor
410
The color to be used for drawing box around text.
411
Either a string (e.g. "yellow") or in 0xRRGGBB[AA] format
412
(e.g. "0xff00ff"), possibly followed by an alpha specifier.
413
The default value of @var{boxcolor} is "white".
414

  
415
@item box
416
Used to draw a box around text using background color.
417
Value should be either 1 (enable) or 0 (disable).
418
The default value of @var{box} is 0.
419

  
420
@item ft_load_flags
421
Flags to be used for loading the fonts.
422

  
423
The flags map the corresponding flags supported by libfreetype, and are
424
a combination of the following values:
425
@table @var
426
@item default
427
@item no_scale
428
@item no_hinting
429
@item render
430
@item no_bitmap
431
@item vertical_layout
432
@item force_autohint
433
@item crop_bitmap
434
@item pedantic
435
@item ignore_global_advance_width
436
@item no_recurse
437
@item ignore_transform
438
@item monochrome
439
@item linear_design
440
@item no_autohint
441
@item end table
442
@end table
443

  
444
Default value is "render".
445

  
446
For more information consult the documentation for the FT_LOAD_*
447
libfreetype flags.
448

  
449
@item tabsize
450
The size in number of spaces to use for rendering the tab.
451
Default value is 4.
452
@end table
453

  
454
For example the command:
455
@example
456
drawtext="fontfile=/usr/share/fonts/truetype/freefont/FreeSerif.ttf: text='Test Text'"
457
@end example
458

  
459
will draw "Test Text" with font FreeSerif, using the default values
460
for the optional parameters.
461

  
462
The command:
463
@example
464
drawtext="fontfile=/usr/share/fonts/truetype/freefont/FreeSerif.ttf: text='Test Text':\
465
          x=100: y=50: fontsize=24: fontcolor=yellow@@0.2: box=1: boxcolor=red@@0.2"
466
@end example
467

  
468
will draw 'Test Text' with font FreeSerif of size 24 at position x=100
469
and y=50 (counting from the top-left corner of the screen), text is
470
yellow with a red box around it. Both the text and the box have an
471
opacity of 20%.
472

  
473
Note that the double quotes are not necessary if spaces are not used
474
within the parameter list.
475

  
476
For more information about libfreetype, check:
477
@url{http://www.freetype.org/}.
478

  
356 479
@section fade
357 480

  
358 481
Apply fade-in/out effect to input video.
libavfilter/Makefile
26 26
OBJS-$(CONFIG_CROP_FILTER)                   += vf_crop.o
27 27
OBJS-$(CONFIG_CROPDETECT_FILTER)             += vf_cropdetect.o
28 28
OBJS-$(CONFIG_DRAWBOX_FILTER)                += vf_drawbox.o
29
OBJS-$(CONFIG_DRAWTEXT_FILTER)               += vf_drawtext.o
29 30
OBJS-$(CONFIG_FADE_FILTER)                   += vf_fade.o
30 31
OBJS-$(CONFIG_FIELDORDER_FILTER)             += vf_fieldorder.o
31 32
OBJS-$(CONFIG_FIFO_FILTER)                   += vf_fifo.o
libavfilter/allfilters.c
45 45
    REGISTER_FILTER (CROP,        crop,        vf);
46 46
    REGISTER_FILTER (CROPDETECT,  cropdetect,  vf);
47 47
    REGISTER_FILTER (DRAWBOX,     drawbox,     vf);
48
    REGISTER_FILTER (DRAWTEXT,    drawtext,    vf);
48 49
    REGISTER_FILTER (FADE,        fade,        vf);
49 50
    REGISTER_FILTER (FIELDORDER,  fieldorder,  vf);
50 51
    REGISTER_FILTER (FIFO,        fifo,        vf);
libavfilter/avfilter.h
26 26
#include "libavutil/samplefmt.h"
27 27

  
28 28
#define LIBAVFILTER_VERSION_MAJOR  2
29
#define LIBAVFILTER_VERSION_MINOR  3
30
#define LIBAVFILTER_VERSION_MICRO  1
29
#define LIBAVFILTER_VERSION_MINOR  4
30
#define LIBAVFILTER_VERSION_MICRO  0
31 31

  
32 32
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
33 33
                                               LIBAVFILTER_VERSION_MINOR, \
libavfilter/vf_drawtext.c
1
/*
2
 * Copyright (c) 2011 Stefano Sabatini
3
 * Copyright (c) 2010 S.N. Hemanth Meenakshisundaram
4
 * Copyright (c) 2003 Gustavo Sverzut Barbieri <gsbarbieri@yahoo.com.br>
5
 *
6
 * This file is part of FFmpeg.
7
 *
8
 * FFmpeg is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public
10
 * License as published by the Free Software Foundation; either
11
 * version 2.1 of the License, or (at your option) any later version.
12
 *
13
 * FFmpeg is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 * Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public
19
 * License along with FFmpeg; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
 */
22

  
23
/**
24
 * @file
25
 * drawtext filter, based on the original FFmpeg vhook/drawtext.c
26
 * filter by Gustavo Sverzut Barbieri
27
 */
28

  
29
#include <sys/time.h>
30
#include <time.h>
31

  
32
#include "libavutil/colorspace.h"
33
#include "libavutil/file.h"
34
#include "libavutil/opt.h"
35
#include "libavutil/parseutils.h"
36
#include "libavutil/pixdesc.h"
37
#include "libavutil/tree.h"
38
#include "avfilter.h"
39
#include "drawutils.h"
40

  
41
#undef time
42

  
43
#include <ft2build.h>
44
#include <freetype/config/ftheader.h>
45
#include FT_FREETYPE_H
46
#include FT_GLYPH_H
47

  
48
typedef struct {
49
    const AVClass *class;
50
    uint8_t *fontfile;              ///< font to be used
51
    uint8_t *text;                  ///< text to be drawn
52
    uint8_t *text_priv;             ///< used to detect whether text changed
53
    int ft_load_flags;              ///< flags used for loading fonts, see FT_LOAD_*
54
    FT_Vector *positions;           ///< positions for each element in the text
55
    char *textfile;                 ///< file with text to be drawn
56
    unsigned int x;                 ///< x position to start drawing text
57
    unsigned int y;                 ///< y position to start drawing text
58
    unsigned int fontsize;          ///< font size to use
59
    char *fontcolor_string;         ///< font color as string
60
    char *boxcolor_string;          ///< box color as string
61
    uint8_t fontcolor[4];           ///< foreground color
62
    uint8_t boxcolor[4];            ///< background color
63
    uint8_t fontcolor_rgba[4];      ///< foreground color in RGBA
64
    uint8_t boxcolor_rgba[4];       ///< background color in RGBA
65

  
66
    short int draw_box;             ///< draw box around text - true or false
67
    int use_kerning;                ///< font kerning is used - true/false
68
    int tabsize;                    ///< tab size
69

  
70
    FT_Library library;             ///< freetype font library handle
71
    FT_Face face;                   ///< freetype font face handle
72
    struct AVTreeNode *glyphs;      ///< rendered glyphs, stored using the UTF-32 char code
73
    int hsub, vsub;                 ///< chroma subsampling values
74
    int is_packed_rgb;
75
    int pixel_step[4];              ///< distance in bytes between the component of each pixel
76
    uint8_t rgba_map[4];            ///< map RGBA offsets to the positions in the packed RGBA format
77
    uint8_t *box_line[4];           ///< line used for filling the box background
78
} DrawTextContext;
79

  
80
#define OFFSET(x) offsetof(DrawTextContext, x)
81

  
82
static const AVOption drawtext_options[]= {
83
{"fontfile", "set font file",        OFFSET(fontfile),         FF_OPT_TYPE_STRING, 0,  CHAR_MIN, CHAR_MAX },
84
{"text",     "set text",             OFFSET(text),             FF_OPT_TYPE_STRING, 0,  CHAR_MIN, CHAR_MAX },
85
{"textfile", "set text file",        OFFSET(textfile),         FF_OPT_TYPE_STRING, 0,  CHAR_MIN, CHAR_MAX },
86
{"fontcolor","set foreground color", OFFSET(fontcolor_string), FF_OPT_TYPE_STRING, 0,  CHAR_MIN, CHAR_MAX },
87
{"boxcolor", "set box color",        OFFSET(boxcolor_string),  FF_OPT_TYPE_STRING, 0,  CHAR_MIN, CHAR_MAX },
88
{"box",      "set box",              OFFSET(draw_box),         FF_OPT_TYPE_INT,    0,         0,        1 },
89
{"fontsize", "set font size",        OFFSET(fontsize),         FF_OPT_TYPE_INT,   16,         1,       72 },
90
{"x",        "set x",                OFFSET(x),                FF_OPT_TYPE_INT,    0,         0,  INT_MAX },
91
{"y",        "set y",                OFFSET(y),                FF_OPT_TYPE_INT,    0,         0,  INT_MAX },
92
{"tabsize",  "set tab size",         OFFSET(tabsize),          FF_OPT_TYPE_INT,    4,         0,  INT_MAX },
93

  
94
/* FT_LOAD_* flags */
95
{"ft_load_flags", "set font loading flags for libfreetype",   OFFSET(ft_load_flags),  FF_OPT_TYPE_FLAGS,  FT_LOAD_DEFAULT|FT_LOAD_RENDER, 0, INT_MAX, 0, "ft_load_flags" },
96
{"default",                     "set default",                     0, FF_OPT_TYPE_CONST, FT_LOAD_DEFAULT,                     INT_MIN, INT_MAX, 0, "ft_load_flags" },
97
{"no_scale",                    "set no_scale",                    0, FF_OPT_TYPE_CONST, FT_LOAD_NO_SCALE,                    INT_MIN, INT_MAX, 0, "ft_load_flags" },
98
{"no_hinting",                  "set no_hinting",                  0, FF_OPT_TYPE_CONST, FT_LOAD_NO_HINTING,                  INT_MIN, INT_MAX, 0, "ft_load_flags" },
99
{"render",                      "set render",                      0, FF_OPT_TYPE_CONST, FT_LOAD_RENDER,                      INT_MIN, INT_MAX, 0, "ft_load_flags" },
100
{"no_bitmap",                   "set no_bitmap",                   0, FF_OPT_TYPE_CONST, FT_LOAD_NO_BITMAP,                   INT_MIN, INT_MAX, 0, "ft_load_flags" },
101
{"vertical_layout",             "set vertical_layout",             0, FF_OPT_TYPE_CONST, FT_LOAD_VERTICAL_LAYOUT,             INT_MIN, INT_MAX, 0, "ft_load_flags" },
102
{"force_autohint",              "set force_autohint",              0, FF_OPT_TYPE_CONST, FT_LOAD_FORCE_AUTOHINT,              INT_MIN, INT_MAX, 0, "ft_load_flags" },
103
{"crop_bitmap",                 "set crop_bitmap",                 0, FF_OPT_TYPE_CONST, FT_LOAD_CROP_BITMAP,                 INT_MIN, INT_MAX, 0, "ft_load_flags" },
104
{"pedantic",                    "set pedantic",                    0, FF_OPT_TYPE_CONST, FT_LOAD_PEDANTIC,                    INT_MIN, INT_MAX, 0, "ft_load_flags" },
105
{"ignore_global_advance_width", "set ignore_global_advance_width", 0, FF_OPT_TYPE_CONST, FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH, INT_MIN, INT_MAX, 0, "ft_load_flags" },
106
{"no_recurse",                  "set no_recurse",                  0, FF_OPT_TYPE_CONST, FT_LOAD_NO_RECURSE,                  INT_MIN, INT_MAX, 0, "ft_load_flags" },
107
{"ignore_transform",            "set ignore_transform",            0, FF_OPT_TYPE_CONST, FT_LOAD_IGNORE_TRANSFORM,            INT_MIN, INT_MAX, 0, "ft_load_flags" },
108
{"monochrome",                  "set monochrome",                  0, FF_OPT_TYPE_CONST, FT_LOAD_MONOCHROME,                  INT_MIN, INT_MAX, 0, "ft_load_flags" },
109
{"linear_design",               "set linear_design",               0, FF_OPT_TYPE_CONST, FT_LOAD_LINEAR_DESIGN,               INT_MIN, INT_MAX, 0, "ft_load_flags" },
110
{"no_autohint",                 "set no_autohint",                 0, FF_OPT_TYPE_CONST, FT_LOAD_NO_AUTOHINT,                 INT_MIN, INT_MAX, 0, "ft_load_flags" },
111
{NULL},
112
};
113

  
114
static const char *drawtext_get_name(void *ctx)
115
{
116
    return "drawtext";
117
}
118

  
119
static const AVClass drawtext_class = {
120
    "DrawTextContext",
121
    drawtext_get_name,
122
    drawtext_options
123
};
124

  
125
#undef __FTERRORS_H__
126
#define FT_ERROR_START_LIST {
127
#define FT_ERRORDEF(e, v, s) { (e), (s) },
128
#define FT_ERROR_END_LIST { 0, NULL } };
129

  
130
struct ft_error
131
{
132
    int err;
133
    const char *err_msg;
134
} static ft_errors[] =
135
#include FT_ERRORS_H
136

  
137
#define FT_ERRMSG(e) ft_errors[e].err_msg
138

  
139
typedef struct {
140
    FT_Glyph *glyph;
141
    uint32_t code;
142
    FT_Bitmap bitmap; ///< array holding bitmaps of font
143
    FT_BBox bbox;
144
    int advance;
145
    int bitmap_left;
146
    int bitmap_top;
147
} Glyph;
148

  
149
static int glyph_cmp(void *key, const void *b)
150
{
151
    const Glyph *a = key, *bb = b;
152
    int64_t diff = (int64_t)a->code - (int64_t)bb->code;
153
    return diff > 0 ? 1 : diff < 0 ? -1 : 0;
154
}
155

  
156
/**
157
 * Load glyphs corresponding to the UTF-32 codepoint code.
158
 */
159
static int load_glyph(AVFilterContext *ctx, Glyph **glyph_ptr, uint32_t code)
160
{
161
    DrawTextContext *dtext = ctx->priv;
162
    Glyph *glyph;
163
    struct AVTreeNode *node = NULL;
164
    int ret;
165

  
166
    /* load glyph into dtext->face->glyph */
167
    if (FT_Load_Char(dtext->face, code, dtext->ft_load_flags))
168
        return AVERROR(EINVAL);
169

  
170
    /* save glyph */
171
    if (!(glyph = av_mallocz(sizeof(*glyph))) ||
172
        !(glyph->glyph = av_mallocz(sizeof(*glyph->glyph)))) {
173
        ret = AVERROR(ENOMEM);
174
        goto error;
175
    }
176
    glyph->code  = code;
177

  
178
    if (FT_Get_Glyph(dtext->face->glyph, glyph->glyph)) {
179
        ret = AVERROR(EINVAL);
180
        goto error;
181
    }
182

  
183
    glyph->bitmap      = dtext->face->glyph->bitmap;
184
    glyph->bitmap_left = dtext->face->glyph->bitmap_left;
185
    glyph->bitmap_top  = dtext->face->glyph->bitmap_top;
186
    glyph->advance     = dtext->face->glyph->advance.x >> 6;
187

  
188
    /* measure text height to calculate text_height (or the maximum text height) */
189
    FT_Glyph_Get_CBox(*glyph->glyph, ft_glyph_bbox_pixels, &glyph->bbox);
190

  
191
    /* cache the newly created glyph */
192
    if (!(node = av_mallocz(av_tree_node_size))) {
193
        ret = AVERROR(ENOMEM);
194
        goto error;
195
    }
196
    av_tree_insert(&dtext->glyphs, glyph, glyph_cmp, &node);
197

  
198
    if (glyph_ptr)
199
        *glyph_ptr = glyph;
200
    return 0;
201

  
202
error:
203
    if (glyph)
204
        av_freep(&glyph->glyph);
205
    av_freep(&glyph);
206
    av_freep(&node);
207
    return ret;
208
}
209

  
210
static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
211
{
212
    int err;
213
    DrawTextContext *dtext = ctx->priv;
214
    Glyph *glyph;
215

  
216
    dtext->class = &drawtext_class;
217
    av_opt_set_defaults2(dtext, 0, 0);
218
    dtext->fontcolor_string = av_strdup("black");
219
    dtext->boxcolor_string = av_strdup("white");
220

  
221
    if ((err = (av_set_options_string(dtext, args, "=", ":"))) < 0) {
222
        av_log(ctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args);
223
        return err;
224
    }
225

  
226
    if (!dtext->fontfile) {
227
        av_log(ctx, AV_LOG_ERROR, "No font filename provided\n");
228
        return AVERROR(EINVAL);
229
    }
230

  
231
    if (dtext->textfile) {
232
        uint8_t *textbuf;
233
        size_t textbuf_size;
234

  
235
        if (dtext->text) {
236
            av_log(ctx, AV_LOG_ERROR,
237
                   "Both text and text file provided. Please provide only one\n");
238
            return AVERROR(EINVAL);
239
        }
240
        if ((err = av_file_map(dtext->textfile, &textbuf, &textbuf_size, 0, ctx)) < 0) {
241
            av_log(ctx, AV_LOG_ERROR,
242
                   "The text file '%s' could not be read or is empty\n",
243
                   dtext->textfile);
244
            return err;
245
        }
246

  
247
        if (!(dtext->text = av_malloc(textbuf_size+1)))
248
            return AVERROR(ENOMEM);
249
        memcpy(dtext->text, textbuf, textbuf_size);
250
        dtext->text[textbuf_size] = 0;
251
        av_file_unmap(textbuf, textbuf_size);
252
    }
253

  
254
    if (!dtext->text) {
255
        av_log(ctx, AV_LOG_ERROR,
256
               "Either text or a valid file must be provided\n");
257
        return AVERROR(EINVAL);
258
    }
259

  
260
    if ((err = av_parse_color(dtext->fontcolor_rgba, dtext->fontcolor_string, -1, ctx))) {
261
        av_log(ctx, AV_LOG_ERROR,
262
               "Invalid font color '%s'\n", dtext->fontcolor_string);
263
        return err;
264
    }
265

  
266
    if ((err = av_parse_color(dtext->boxcolor_rgba, dtext->boxcolor_string, -1, ctx))) {
267
        av_log(ctx, AV_LOG_ERROR,
268
               "Invalid box color '%s'\n", dtext->boxcolor_string);
269
        return err;
270
    }
271

  
272
    if ((err = FT_Init_FreeType(&(dtext->library)))) {
273
        av_log(ctx, AV_LOG_ERROR,
274
               "Could not load FreeType: %s\n", FT_ERRMSG(err));
275
        return AVERROR(EINVAL);
276
    }
277

  
278
    /* load the face, and set up the encoding, which is by default UTF-8 */
279
    if ((err = FT_New_Face(dtext->library, dtext->fontfile, 0, &dtext->face))) {
280
        av_log(ctx, AV_LOG_ERROR, "Could not load fontface from file '%s': %s\n",
281
               dtext->fontfile, FT_ERRMSG(err));
282
        return AVERROR(EINVAL);
283
    }
284
    if ((err = FT_Set_Pixel_Sizes(dtext->face, 0, dtext->fontsize))) {
285
        av_log(ctx, AV_LOG_ERROR, "Could not set font size to %d pixels: %s\n",
286
               dtext->fontsize, FT_ERRMSG(err));
287
        return AVERROR(EINVAL);
288
    }
289

  
290
    dtext->use_kerning = FT_HAS_KERNING(dtext->face);
291

  
292
    /* load the fallback glyph with code 0 */
293
    load_glyph(ctx, NULL, 0);
294

  
295
    /* set the tabsize in pixels */
296
    if ((err = load_glyph(ctx, &glyph, ' ') < 0)) {
297
        av_log(ctx, AV_LOG_ERROR, "Could not set tabsize.\n");
298
        return err;
299
    }
300
    dtext->tabsize *= glyph->advance;
301

  
302
#if !HAVE_LOCALTIME_R
303
    av_log(ctx, AV_LOG_WARNING, "strftime() expansion unavailable!\n");
304
#endif
305

  
306
    return 0;
307
}
308

  
309
static int query_formats(AVFilterContext *ctx)
310
{
311
    static const enum PixelFormat pix_fmts[] = {
312
        PIX_FMT_ARGB,    PIX_FMT_RGBA,
313
        PIX_FMT_ABGR,    PIX_FMT_BGRA,
314
        PIX_FMT_RGB24,   PIX_FMT_BGR24,
315
        PIX_FMT_YUV420P, PIX_FMT_YUV444P,
316
        PIX_FMT_YUV422P, PIX_FMT_YUV411P,
317
        PIX_FMT_YUV410P, PIX_FMT_YUV440P,
318
        PIX_FMT_NONE
319
    };
320

  
321
    avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts));
322
    return 0;
323
}
324

  
325
static int glyph_enu_free(void *opaque, void *elem)
326
{
327
    av_free(elem);
328
    return 0;
329
}
330

  
331
static av_cold void uninit(AVFilterContext *ctx)
332
{
333
    DrawTextContext *dtext = ctx->priv;
334
    int i;
335

  
336
    av_freep(&dtext->fontfile);
337
    av_freep(&dtext->text);
338
    av_freep(&dtext->fontcolor_string);
339
    av_freep(&dtext->boxcolor_string);
340
    av_freep(&dtext->positions);
341
    av_tree_enumerate(dtext->glyphs, NULL, NULL, glyph_enu_free);
342
    av_tree_destroy(dtext->glyphs);
343
    dtext->glyphs = 0;
344
    FT_Done_Face(dtext->face);
345
    FT_Done_FreeType(dtext->library);
346

  
347
    for (i = 0; i < 4; i++) {
348
        av_freep(&dtext->box_line[i]);
349
        dtext->pixel_step[i] = 0;
350
    }
351

  
352
}
353

  
354
static int config_input(AVFilterLink *inlink)
355
{
356
    DrawTextContext *dtext = inlink->dst->priv;
357
    const AVPixFmtDescriptor *pix_desc = &av_pix_fmt_descriptors[inlink->format];
358
    int ret;
359

  
360
    dtext->hsub = pix_desc->log2_chroma_w;
361
    dtext->vsub = pix_desc->log2_chroma_h;
362

  
363
    if ((ret =
364
         ff_fill_line_with_color(dtext->box_line, dtext->pixel_step,
365
                                 inlink->w, dtext->boxcolor,
366
                                 inlink->format, dtext->boxcolor_rgba,
367
                                 &dtext->is_packed_rgb, dtext->rgba_map)) < 0)
368
        return ret;
369

  
370
    if (!dtext->is_packed_rgb) {
371
        uint8_t *rgba = dtext->fontcolor_rgba;
372
        dtext->fontcolor[0] = RGB_TO_Y_CCIR(rgba[0], rgba[1], rgba[2]);
373
        dtext->fontcolor[1] = RGB_TO_U_CCIR(rgba[0], rgba[1], rgba[2], 0);
374
        dtext->fontcolor[2] = RGB_TO_V_CCIR(rgba[0], rgba[1], rgba[2], 0);
375
        dtext->fontcolor[3] = rgba[3];
376
    }
377

  
378
    return 0;
379
}
380

  
381
#define GET_BITMAP_VAL(r, c)                                            \
382
    bitmap->pixel_mode == FT_PIXEL_MODE_MONO ?                          \
383
        (bitmap->buffer[(r) * bitmap->pitch + ((c)>>3)] & (0x80 >> ((c)&7))) * 255 : \
384
         bitmap->buffer[(r) * bitmap->pitch +  (c)]
385

  
386
#define SET_PIXEL_YUV(picref, yuva_color, val, x, y, hsub, vsub) {           \
387
    luma_pos    = ((x)          ) + ((y)          ) * picref->linesize[0]; \
388
    chroma_pos1 = ((x) >> (hsub)) + ((y) >> (vsub)) * picref->linesize[1]; \
389
    chroma_pos2 = ((x) >> (hsub)) + ((y) >> (vsub)) * picref->linesize[2]; \
390
    alpha = (yuva_color[3] * (val)) / 255;                               \
391
    picref->data[0][luma_pos] = (alpha * yuva_color[0] + (255 - alpha) * picref->data[0][luma_pos]) >> 8; \
392
    alpha = (yuva_color[3] * (val)) / 224;                               \
393
    picref->data[1][chroma_pos1] = 16 + (alpha * (yuva_color[1]-16) + (224 - alpha) * (picref->data[1][chroma_pos1]-16)) / 224; \
394
    picref->data[2][chroma_pos2] = 16 + (alpha * (yuva_color[2]-16) + (224 - alpha) * (picref->data[2][chroma_pos2]-16)) / 224; \
395
}
396

  
397
static inline int draw_glyph_yuv(AVFilterBufferRef *picref, FT_Bitmap *bitmap, unsigned int x,
398
                                 unsigned int y, unsigned int width, unsigned int height,
399
                                 const uint8_t yuva_color[4], int hsub, int vsub)
400
{
401
    int r, c, alpha;
402
    unsigned int luma_pos, chroma_pos1, chroma_pos2;
403
    uint8_t src_val, dst_pixel[4];
404

  
405
    for (r = 0; r < bitmap->rows && r+y < height; r++) {
406
        for (c = 0; c < bitmap->width && c+x < width; c++) {
407
            /* get pixel in the picref (destination) */
408
            dst_pixel[0] = picref->data[0][  c+x           +  (y+r)          * picref->linesize[0]];
409
            dst_pixel[1] = picref->data[1][((c+x) >> hsub) + ((y+r) >> vsub) * picref->linesize[1]];
410
            dst_pixel[2] = picref->data[2][((c+x) >> hsub) + ((y+r) >> vsub) * picref->linesize[2]];
411

  
412
            /* get intensity value in the glyph bitmap (source) */
413
            src_val = GET_BITMAP_VAL(r, c);
414
            if (!src_val)
415
                continue;
416

  
417
            SET_PIXEL_YUV(picref, yuva_color, src_val, c+x, y+r, hsub, vsub);
418
        }
419
    }
420

  
421
    return 0;
422
}
423

  
424
#define SET_PIXEL_RGB(picref, rgba_color, val, x, y, pixel_step, r_off, g_off, b_off, a_off) { \
425
    p   = picref->data[0] + (x) * pixel_step + ((y) * picref->linesize[0]); \
426
    alpha = (rgba_color[3] * (val)) / 255;                              \
427
    *(p+r_off) = (alpha * rgba_color[0] + (255 - alpha) * *(p+r_off)) >> 8; \
428
    *(p+g_off) = (alpha * rgba_color[1] + (255 - alpha) * *(p+g_off)) >> 8; \
429
    *(p+b_off) = (alpha * rgba_color[2] + (255 - alpha) * *(p+b_off)) >> 8; \
430
}
431

  
432
static inline int draw_glyph_rgb(AVFilterBufferRef *picref, FT_Bitmap *bitmap,
433
                                 unsigned int x, unsigned int y,
434
                                 unsigned int width, unsigned int height, int pixel_step,
435
                                 const uint8_t rgba_color[4], const uint8_t rgba_map[4])
436
{
437
    int r, c, alpha;
438
    uint8_t *p;
439
    uint8_t src_val, dst_pixel[4];
440

  
441
    for (r = 0; r < bitmap->rows && r+y < height; r++) {
442
        for (c = 0; c < bitmap->width && c+x < width; c++) {
443
            /* get pixel in the picref (destination) */
444
            dst_pixel[0] = picref->data[0][(c+x + rgba_map[0]) * pixel_step +
445
                                           (y+r) * picref->linesize[0]];
446
            dst_pixel[1] = picref->data[0][(c+x + rgba_map[1]) * pixel_step +
447
                                           (y+r) * picref->linesize[0]];
448
            dst_pixel[2] = picref->data[0][(c+x + rgba_map[2]) * pixel_step +
449
                                           (y+r) * picref->linesize[0]];
450

  
451
            /* get intensity value in the glyph bitmap (source) */
452
            src_val = GET_BITMAP_VAL(r, c);
453
            if (!src_val)
454
                continue;
455

  
456
            SET_PIXEL_RGB(picref, rgba_color, src_val, c+x, y+r, pixel_step,
457
                          rgba_map[0], rgba_map[1], rgba_map[2], rgba_map[3]);
458
        }
459
    }
460

  
461
    return 0;
462
}
463

  
464
static inline void drawbox(AVFilterBufferRef *picref, unsigned int x, unsigned int y,
465
                           unsigned int width, unsigned int height,
466
                           uint8_t *line[4], int pixel_step[4], uint8_t color[4],
467
                           int hsub, int vsub, int is_rgba_packed, uint8_t rgba_map[4])
468
{
469
    int i, j, alpha;
470

  
471
    if (color[3] != 0xFF) {
472
        if (is_rgba_packed) {
473
            uint8_t *p;
474
            for (j = 0; j < height; j++)
475
                for (i = 0; i < width; i++)
476
                    SET_PIXEL_RGB(picref, color, 255, i+x, y+j, pixel_step[0],
477
                                  rgba_map[0], rgba_map[1], rgba_map[2], rgba_map[3]);
478
        } else {
479
            unsigned int luma_pos, chroma_pos1, chroma_pos2;
480
            for (j = 0; j < height; j++)
481
                for (i = 0; i < width; i++)
482
                    SET_PIXEL_YUV(picref, color, 255, i+x, y+j, hsub, vsub);
483
        }
484
    } else {
485
        ff_draw_rectangle(picref->data, picref->linesize,
486
                          line, pixel_step, hsub, vsub,
487
                          x, y, width, height);
488
    }
489
}
490

  
491
static inline int is_newline(uint32_t c)
492
{
493
    return (c == '\n' || c == '\r' || c == '\f' || c == '\v');
494
}
495

  
496
static int draw_text(AVFilterContext *ctx, AVFilterBufferRef *picref,
497
                     int width, int height)
498
{
499
    DrawTextContext *dtext = ctx->priv;
500
    uint32_t code = 0, prev_code = 0;
501
    int x = 0, y = 0, i = 0;
502
    int text_height, baseline;
503
    uint8_t *p;
504
    int str_w = 0;
505
    int y_min = 32000, y_max = -32000;
506
    FT_Vector delta;
507
    Glyph *glyph = NULL, *prev_glyph = NULL;
508
    Glyph dummy = { 0 };
509

  
510
    if (dtext->text != dtext->text_priv) {
511
#if HAVE_LOCALTIME_R
512
        time_t now = time(0);
513
        struct tm ltime;
514
        uint8_t *buf = NULL;
515
        int     buflen = 2*strlen(dtext->text) + 1, len;
516

  
517
        localtime_r(&now, &ltime);
518

  
519
        while ((buf = av_realloc(buf, buflen))) {
520
            *buf = 1;
521
            if ((len = strftime(buf, buflen, dtext->text, &ltime)) != 0 || *buf == 0)
522
                break;
523
            buflen *= 2;
524
        }
525
        if (!buf)
526
            return AVERROR(ENOMEM);
527
        av_freep(&dtext->text);
528
        dtext->text = dtext->text_priv = buf;
529
#else
530
        dtext->text_priv = dtext->text;
531
#endif
532
        if (!(dtext->positions = av_realloc(dtext->positions,
533
                                            strlen(dtext->text)*sizeof(*dtext->positions))))
534
            return AVERROR(ENOMEM);
535
    }
536

  
537
    x = dtext->x;
538
    y = dtext->y;
539

  
540
    /* load and cache glyphs */
541
    for (i = 0, p = dtext->text; *p; i++) {
542
        GET_UTF8(code, *p++, continue;);
543

  
544
        /* get glyph */
545
        dummy.code = code;
546
        glyph = av_tree_find(dtext->glyphs, &dummy, glyph_cmp, NULL);
547
        if (!glyph)
548
            load_glyph(ctx, &glyph, code);
549

  
550
        y_min = FFMIN(glyph->bbox.yMin, y_min);
551
        y_max = FFMAX(glyph->bbox.yMax, y_max);
552
    }
553
    text_height = y_max - y_min;
554
    baseline    = y_max;
555

  
556
    /* compute and save position for each glyph */
557
    glyph = NULL;
558
    for (i = 0, p = dtext->text; *p; i++) {
559
        GET_UTF8(code, *p++, continue;);
560

  
561
        /* skip the \n in the sequence \r\n */
562
        if (prev_code == '\r' && code == '\n')
563
            continue;
564

  
565
        prev_code = code;
566
        if (is_newline(code)) {
567
            str_w = FFMAX(str_w, x - dtext->x);
568
            y += text_height;
569
            x = dtext->x;
570
            continue;
571
        }
572

  
573
        /* get glyph */
574
        prev_glyph = glyph;
575
        dummy.code = code;
576
        glyph = av_tree_find(dtext->glyphs, &dummy, glyph_cmp, NULL);
577

  
578
        /* kerning */
579
        if (dtext->use_kerning && prev_glyph && glyph->code) {
580
            FT_Get_Kerning(dtext->face, prev_glyph->code, glyph->code,
581
                           ft_kerning_default, &delta);
582
            x += delta.x >> 6;
583
        }
584

  
585
        if (x + glyph->bbox.xMax >= width) {
586
            str_w = FFMAX(str_w, x - dtext->x);
587
            y += text_height;
588
            x = dtext->x;
589
        }
590

  
591
        /* save position */
592
        dtext->positions[i].x = x + glyph->bitmap_left;
593
        dtext->positions[i].y = y - glyph->bitmap_top + baseline;
594
        if (code == '\t') x  = (x / dtext->tabsize + 1)*dtext->tabsize;
595
        else              x += glyph->advance;
596
    }
597

  
598
    str_w = FFMIN(width - dtext->x - 1, FFMAX(str_w, x - dtext->x));
599
    y     = FFMIN(y + text_height, height - 1);
600

  
601
    /* draw box */
602
    if (dtext->draw_box)
603
        drawbox(picref, dtext->x, dtext->y, str_w, y-dtext->y,
604
                dtext->box_line, dtext->pixel_step, dtext->boxcolor,
605
                dtext->hsub, dtext->vsub, dtext->is_packed_rgb, dtext->rgba_map);
606

  
607
    /* draw glyphs */
608
    for (i = 0, p = dtext->text; *p; i++) {
609
        Glyph dummy = { 0 };
610
        GET_UTF8(code, *p++, continue;);
611

  
612
        /* skip new line chars, just go to new line */
613
        if (is_newline(code) || code == ' ' || code == '\t')
614
            continue;
615

  
616
        dummy.code = code;
617
        glyph = av_tree_find(dtext->glyphs, &dummy, glyph_cmp, NULL);
618

  
619
        if (glyph->bitmap.pixel_mode != FT_PIXEL_MODE_MONO &&
620
            glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
621
            return AVERROR(EINVAL);
622

  
623
        if (dtext->is_packed_rgb) {
624
            draw_glyph_rgb(picref, &glyph->bitmap,
625
                           dtext->positions[i].x, dtext->positions[i].y, width, height,
626
                           dtext->pixel_step[0], dtext->fontcolor_rgba, dtext->rgba_map);
627
        } else {
628
            draw_glyph_yuv(picref, &glyph->bitmap,
629
                           dtext->positions[i].x, dtext->positions[i].y, width, height,
630
                           dtext->fontcolor, dtext->hsub, dtext->vsub);
631
        }
632
    }
633

  
634
    return 0;
635
}
636

  
637
static void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { }
638

  
639
static void end_frame(AVFilterLink *inlink)
640
{
641
    AVFilterLink *outlink = inlink->dst->outputs[0];
642
    AVFilterBufferRef *picref = inlink->cur_buf;
643

  
644
    draw_text(inlink->dst, picref, picref->video->w, picref->video->h);
645

  
646
    avfilter_draw_slice(outlink, 0, picref->video->h, 1);
647
    avfilter_end_frame(outlink);
648
}
649

  
650
AVFilter avfilter_vf_drawtext = {
651
    .name          = "drawtext",
652
    .description   = NULL_IF_CONFIG_SMALL("Draw text on top of video frames using libfreetype library."),
653
    .priv_size     = sizeof(DrawTextContext),
654
    .init          = init,
655
    .uninit        = uninit,
656
    .query_formats = query_formats,
657

  
658
    .inputs    = (AVFilterPad[]) {{ .name             = "default",
659
                                    .type             = AVMEDIA_TYPE_VIDEO,
660
                                    .get_video_buffer = avfilter_null_get_video_buffer,
661
                                    .start_frame      = avfilter_null_start_frame,
662
                                    .draw_slice       = null_draw_slice,
663
                                    .end_frame        = end_frame,
664
                                    .config_props     = config_input,
665
                                    .min_perms        = AV_PERM_WRITE |
666
                                                        AV_PERM_READ,
667
                                    .rej_perms        = AV_PERM_PRESERVE },
668
                                  { .name = NULL}},
669
    .outputs   = (AVFilterPad[]) {{ .name             = "default",
670
                                    .type             = AVMEDIA_TYPE_VIDEO, },
671
                                  { .name = NULL}},
672
};

Also available in: Unified diff