From 8a552f950aae0b71ad3fbf6be118ad166aab9b44 Mon Sep 17 00:00:00 2001
From: ddosvulnerability <anurag.singh.phy15@iitbhu.ac.in>
Date: Wed, 18 Apr 2018 14:21:30 +0530
Subject: [PATCH] avfilter: add hellosubs filter.

---
 configure                  |   4 +
 libavfilter/Makefile       |   1 +
 libavfilter/allfilters.c   |   1 +
 libavfilter/vf_hellosubs.c | 960 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 966 insertions(+)
 create mode 100644 libavfilter/vf_hellosubs.c

diff --git a/configure b/configure
index 535a59b..86ebe07 100755
--- a/configure
+++ b/configure
@@ -231,7 +231,9 @@ External library support:
   --enable-libfdk-aac      enable AAC de/encoding via libfdk-aac [no]
   --enable-libflite        enable flite (voice synthesis) support via libflite [no]
   --enable-libfontconfig   enable libfontconfig, useful for drawtext filter [no]
+  --enable-libfontconfig   enable libfontconfig, useful for hellosubs filter [no]
   --enable-libfreetype     enable libfreetype, needed for drawtext filter [no]
+  --enable-libfreetype     enable libfreetype, needed for hellosubs filter [no]
   --enable-libfribidi      enable libfribidi, improves drawtext filter [no]
   --enable-libgme          enable Game Music Emu via libgme [no]
   --enable-libgsm          enable GSM de/encoding via libgsm [no]
@@ -3315,7 +3317,9 @@ delogo_filter_deps="gpl"
 denoise_vaapi_filter_deps="vaapi VAProcPipelineParameterBuffer"
 deshake_filter_select="pixelutils"
 drawtext_filter_deps="libfreetype"
+hellosubs_filter_deps="libfreetype"
 drawtext_filter_suggest="libfontconfig libfribidi"
+hellosubs_filter_suggest="libfontconfig"
 elbg_filter_deps="avcodec"
 eq_filter_deps="gpl"
 fftfilt_filter_deps="avcodec"
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 7fc3de3..3903136 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -192,6 +192,7 @@ OBJS-$(CONFIG_DRAWBOX_FILTER)                += vf_drawbox.o
 OBJS-$(CONFIG_DRAWGRAPH_FILTER)              += f_drawgraph.o
 OBJS-$(CONFIG_DRAWGRID_FILTER)               += vf_drawbox.o
 OBJS-$(CONFIG_DRAWTEXT_FILTER)               += vf_drawtext.o
+OBJS-$(CONFIG_HELLOSUBS_FILTER)               += vf_hellosubs.o
 OBJS-$(CONFIG_EDGEDETECT_FILTER)             += vf_edgedetect.o
 OBJS-$(CONFIG_ELBG_FILTER)                   += vf_elbg.o
 OBJS-$(CONFIG_ENTROPY_FILTER)                += vf_entropy.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index bd55463..a3c6211 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -184,6 +184,7 @@ extern AVFilter ff_vf_drawbox;
 extern AVFilter ff_vf_drawgraph;
 extern AVFilter ff_vf_drawgrid;
 extern AVFilter ff_vf_drawtext;
+extern AVFilter ff_vf_hellosubs;
 extern AVFilter ff_vf_edgedetect;
 extern AVFilter ff_vf_elbg;
 extern AVFilter ff_vf_entropy;
diff --git a/libavfilter/vf_hellosubs.c b/libavfilter/vf_hellosubs.c
new file mode 100644
index 0000000..a2d5074
--- /dev/null
+++ b/libavfilter/vf_hellosubs.c
@@ -0,0 +1,960 @@
+/*
+ * 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
+ * Libfreetype subtitles burning filter. 
+ * @see{http://www.matroska.org/technical/specs/subtitles/ssa.html}
+ */
+
+#include "config.h"
+#include <stdio.h>
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fenv.h>
+#if CONFIG_LIBFONTCONFIG
+#include <fontconfig/fontconfig.h>
+#endif
+#include "libavutil/avstring.h"
+#include "libavutil/bprint.h"
+#include "libavutil/common.h"
+#include "libavutil/file.h"
+#include "libavutil/eval.h"
+#include "libavutil/opt.h"
+#include "libavutil/random_seed.h"
+#include "libavutil/parseutils.h"
+#include "libavutil/timecode.h"
+#include "libavutil/time_internal.h"
+#include "libavutil/tree.h"
+#include "libavutil/lfg.h"
+#include "avfilter.h"
+#include "drawutils.h"
+#include "formats.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",
+    "line_h", "lh",           ///< line height, same as max_glyph_h
+    "main_h", "h", "H",       ///< height of the input video
+    "main_w", "w", "W",       ///< width  of the input video
+    "max_glyph_a", "ascent",  ///< max glyph ascent
+    "max_glyph_d", "descent", ///< min glyph descent
+    "max_glyph_h",            ///< max glyph height
+    "max_glyph_w",            ///< max glyph width
+    "n",                      ///< number of frame
+    "sar",
+    "t",                      ///< timestamp expressed in seconds
+    "text_h", "th",           ///< height of the rendered text
+    "text_w", "tw",           ///< width  of the rendered text
+    "x",
+    "y",
+    "pict_type",
+    NULL
+};
+
+static const char *const fun2_names[] = {
+    "rand"
+};
+
+static double drand(void *opaque, double min, double max)
+{
+    return min + (max-min) / UINT_MAX * av_lfg_get(opaque);
+}
+
+typedef double (*eval_func2)(void *, double a, double b);
+
+static const eval_func2 fun2[] = {
+    drand,
+    NULL
+};
+
+enum var_name {
+    VAR_DAR,
+    VAR_HSUB, VAR_VSUB,
+    VAR_LINE_H, VAR_LH,
+    VAR_MAIN_H, VAR_h, VAR_H,
+    VAR_MAIN_W, VAR_w, VAR_W,
+    VAR_MAX_GLYPH_A, VAR_ASCENT,
+    VAR_MAX_GLYPH_D, VAR_DESCENT,
+    VAR_MAX_GLYPH_H,
+    VAR_MAX_GLYPH_W,
+    VAR_N,
+    VAR_SAR,
+    VAR_T,
+    VAR_TEXT_H, VAR_TH,
+    VAR_TEXT_W, VAR_TW,
+    VAR_X,
+    VAR_Y,
+    VAR_PICT_TYPE,
+    VAR_VARS_NB
+};
+
+
+
+typedef struct hellosubsContext {
+    const AVClass *class;
+    
+    int reinit;                     ///< tells if the filter is being reinited
+#if CONFIG_LIBFONTCONFIG
+    uint8_t *font;              ///< font to be used
+#endif
+    uint8_t *fontfile;              ///< font to be used
+    uint8_t *text;                  ///< text to be drawn
+    AVBPrint expanded_text;         ///< used to contain the expanded text
+    uint8_t *fontcolor_expr;        ///< fontcolor expression to evaluate
+    AVBPrint expanded_fontcolor;    ///< used to contain the expanded fontcolor spec
+    int ft_load_flags;              ///< flags used for loading fonts, see FT_LOAD_*
+    FT_Vector *positions;           ///< positions for each element in the text
+    size_t nb_positions;            ///< number of elements of positions array
+    int x;                          ///< x position to start drawing text
+    int y;                          ///< y position to start drawing text
+    int max_glyph_w;                ///< max glyph width
+    int max_glyph_h;                ///< max glyph height
+    int borderw;                    ///< border width
+    char *fontsize_expr;            ///< expression for fontsize
+    AVExpr *fontsize_pexpr;         ///< parsed expressions for fontsize
+    unsigned int fontsize;          ///< font size to use
+    unsigned int default_fontsize;  ///< default font size to use
+    int line_spacing;               ///< lines spacing in pixels
+    int use_kerning;                ///< font kerning is used - true/false
+    int tabsize;                    ///< tab size
+    int fix_bounds;                 ///< do we let it go out of frame bounds - t/f
+
+    FFDrawContext dc;
+    FFDrawColor fontcolor;          ///< foreground color
+    FT_Library library;             ///< freetype font library handle
+    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
+    char *x_expr;                   ///< expression for x position
+    char *y_expr;                   ///< expression for y position
+    AVExpr *x_pexpr, *y_pexpr;      ///< parsed expressions for x and y
+    int64_t basetime;               ///< base pts time in the real world for display
+    double var_values[VAR_VARS_NB];
+    char   *a_expr;
+    AVExpr *a_pexpr;
+    int alpha;
+    AVLFG  prng;                    ///< random
+    char       *tc_opt_string;      ///< specified timecode option string
+    AVRational  tc_rate;            ///< frame rate for timecode
+    AVTimecode  tc;                 ///< timecode context
+    int tc24hmax;                   ///< 1 if timecode is wrapped to 24 hours, 0 otherwise
+    int reload;                     ///< reload text file for each frame
+    int start_number;               ///< starting frame number for n/frame_num var
+
+    AVDictionary *metadata;
+} hellosubsContext;
+
+#define OFFSET(x) offsetof(hellosubsContext, x)
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+
+static const AVOption hellosubs_options[]= {
+    {"fontfile",    "set font file",        OFFSET(fontfile),           AV_OPT_TYPE_STRING, {.str=NULL},  CHAR_MIN, CHAR_MAX, FLAGS},
+    {"text",        "set text",             OFFSET(text),               AV_OPT_TYPE_STRING, {.str="Hello world"},  CHAR_MIN, CHAR_MAX, FLAGS},
+    {"fontcolor",   "set foreground color", OFFSET(fontcolor.rgba),     AV_OPT_TYPE_COLOR,  {.str="white"}, CHAR_MIN, CHAR_MAX, FLAGS},
+    {"fontcolor_expr", "set foreground color expression", OFFSET(fontcolor_expr), AV_OPT_TYPE_STRING, {.str=""}, CHAR_MIN, CHAR_MAX, FLAGS},
+    {"line_spacing",  "set line spacing in pixels", OFFSET(line_spacing),   AV_OPT_TYPE_INT,    {.i64=0},     INT_MIN,  INT_MAX,FLAGS},
+    {"fontsize",    "set font size",        OFFSET(fontsize_expr),      AV_OPT_TYPE_STRING, {.str="h/20"},  CHAR_MIN, CHAR_MAX , FLAGS},
+    {"x",           "set x expression",     OFFSET(x_expr),             AV_OPT_TYPE_STRING, {.str="w/2.7"},   CHAR_MIN, CHAR_MAX, FLAGS},
+    {"y",           "set y expression",     OFFSET(y_expr),             AV_OPT_TYPE_STRING, {.str="h/1.3"},   CHAR_MIN, CHAR_MAX, FLAGS},
+    {"borderw",     "set border width",     OFFSET(borderw),            AV_OPT_TYPE_INT,    {.i64=0},     INT_MIN,  INT_MAX , FLAGS},
+    {"tabsize",     "set tab size",         OFFSET(tabsize),            AV_OPT_TYPE_INT,    {.i64=4},     0,        INT_MAX , FLAGS},
+    {"basetime",    "set base time",        OFFSET(basetime),           AV_OPT_TYPE_INT64,  {.i64=AV_NOPTS_VALUE}, INT64_MIN, INT64_MAX , FLAGS},
+#if CONFIG_LIBFONTCONFIG
+    { "font",        "Font name",            OFFSET(font),               AV_OPT_TYPE_STRING, { .str = "Sans" },           .flags = FLAGS },
+#endif
+    {"timecode",        "set initial timecode",             OFFSET(tc_opt_string), AV_OPT_TYPE_STRING,   {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS},
+    {"tc24hmax",        "set 24 hours max (timecode only)", OFFSET(tc24hmax),      AV_OPT_TYPE_BOOL,     {.i64=0},           0,        1, FLAGS},
+    {"timecode_rate",   "set rate (timecode only)",         OFFSET(tc_rate),       AV_OPT_TYPE_RATIONAL, {.dbl=0},           0,  INT_MAX, FLAGS},
+    {"r",               "set rate (timecode only)",         OFFSET(tc_rate),       AV_OPT_TYPE_RATIONAL, {.dbl=0},           0,  INT_MAX, FLAGS},
+    {"rate",            "set rate (timecode only)",         OFFSET(tc_rate),       AV_OPT_TYPE_RATIONAL, {.dbl=0},           0,  INT_MAX, FLAGS},
+    {"reload",     "reload text file for each frame",                       OFFSET(reload),     AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    { "alpha",       "apply alpha while rendering", OFFSET(a_expr),      AV_OPT_TYPE_STRING, { .str = "1"     },          .flags = FLAGS },
+    {"fix_bounds", "check and fix text coords to avoid clipping", OFFSET(fix_bounds), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+    {"start_number", "start frame number for n/frame_num variable", OFFSET(start_number), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS},
+
+
+
+    /* FT_LOAD_* flags */
+    { "ft_load_flags", "set font loading flags for libfreetype", OFFSET(ft_load_flags), AV_OPT_TYPE_FLAGS, { .i64 = FT_LOAD_DEFAULT }, 0, INT_MAX, FLAGS, "ft_load_flags" },
+        { "default",                     NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_DEFAULT },                     .flags = FLAGS, .unit = "ft_load_flags" },
+        { "no_scale",                    NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_SCALE },                    .flags = FLAGS, .unit = "ft_load_flags" },
+        { "no_hinting",                  NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_HINTING },                  .flags = FLAGS, .unit = "ft_load_flags" },
+        { "render",                      NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_RENDER },                      .flags = FLAGS, .unit = "ft_load_flags" },
+        { "no_bitmap",                   NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_BITMAP },                   .flags = FLAGS, .unit = "ft_load_flags" },
+        { "vertical_layout",             NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_VERTICAL_LAYOUT },             .flags = FLAGS, .unit = "ft_load_flags" },
+        { "force_autohint",              NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_FORCE_AUTOHINT },              .flags = FLAGS, .unit = "ft_load_flags" },
+        { "crop_bitmap",                 NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_CROP_BITMAP },                 .flags = FLAGS, .unit = "ft_load_flags" },
+        { "pedantic",                    NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_PEDANTIC },                    .flags = FLAGS, .unit = "ft_load_flags" },
+        { "ignore_global_advance_width", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH }, .flags = FLAGS, .unit = "ft_load_flags" },
+        { "no_recurse",                  NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_RECURSE },                  .flags = FLAGS, .unit = "ft_load_flags" },
+        { "ignore_transform",            NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_IGNORE_TRANSFORM },            .flags = FLAGS, .unit = "ft_load_flags" },
+        { "monochrome",                  NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_MONOCHROME },                  .flags = FLAGS, .unit = "ft_load_flags" },
+        { "linear_design",               NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_LINEAR_DESIGN },               .flags = FLAGS, .unit = "ft_load_flags" },
+        { "no_autohint",                 NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_AUTOHINT },                 .flags = FLAGS, .unit = "ft_load_flags" },
+    { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(hellosubs);
+
+#undef __FTERRORS_H__
+#define FT_ERROR_START_LIST {
+#define FT_ERRORDEF(e, v, s) { (e), (s) },
+#define FT_ERROR_END_LIST { 0, NULL } };
+
+static const struct ft_error {
+    int err;
+    const char *err_msg;
+} ft_errors[] =
+#include FT_ERRORS_H
+
+#define FT_ERRMSG(e) ft_errors[e].err_msg
+
+typedef struct Glyph {
+    FT_Glyph glyph;
+    FT_Glyph border_glyph;
+    uint32_t code;
+    unsigned int fontsize;
+    FT_Bitmap bitmap; ///< array holding bitmaps of font
+    FT_Bitmap border_bitmap; ///< array holding bitmaps of font border
+    FT_BBox bbox;
+    int advance;
+    int bitmap_left;
+    int bitmap_top;
+} Glyph;
+
+static int glyph_cmp(const void *key, const void *b)
+{
+    const Glyph *a = key, *bb = b;
+    int64_t diff = (int64_t)a->code - (int64_t)bb->code;
+
+    if (diff != 0)
+         return diff > 0 ? 1 : -1;
+    else
+         return FFDIFFSIGN((int64_t)a->fontsize, (int64_t)bb->fontsize);
+}
+
+/**
+ * Load glyphs corresponding to the UTF-32 codepoint code.
+ */
+static int load_glyph(AVFilterContext *ctx, Glyph **glyph_ptr, uint32_t code)
+{
+    hellosubsContext *s = ctx->priv;
+    FT_BitmapGlyph bitmapglyph;
+    Glyph *glyph;
+    struct AVTreeNode *node = NULL;
+    int ret;
+
+    /* load glyph into s->face->glyph */
+    if (FT_Load_Char(s->face, code, s->ft_load_flags))
+        return AVERROR(EINVAL);
+
+    glyph = av_mallocz(sizeof(*glyph));
+    if (!glyph) {
+        ret = AVERROR(ENOMEM);
+        goto error;
+    }
+    glyph->code  = code;
+    glyph->fontsize = s->fontsize;
+
+    if (FT_Get_Glyph(s->face->glyph, &glyph->glyph)) {
+        ret = AVERROR(EINVAL);
+        goto error;
+    }
+    if (s->borderw) {
+        glyph->border_glyph = glyph->glyph;
+        if (FT_Glyph_StrokeBorder(&glyph->border_glyph, s->stroker, 0, 0) ||
+            FT_Glyph_To_Bitmap(&glyph->border_glyph, FT_RENDER_MODE_NORMAL, 0, 1)) {
+            ret = AVERROR_EXTERNAL;
+            goto error;
+        }
+        bitmapglyph = (FT_BitmapGlyph) glyph->border_glyph;
+        glyph->border_bitmap = bitmapglyph->bitmap;
+    }
+    if (FT_Glyph_To_Bitmap(&glyph->glyph, FT_RENDER_MODE_NORMAL, 0, 1)) {
+        ret = AVERROR_EXTERNAL;
+        goto error;
+    }
+    bitmapglyph = (FT_BitmapGlyph) glyph->glyph;
+
+    glyph->bitmap      = bitmapglyph->bitmap;
+    glyph->bitmap_left = bitmapglyph->left;
+    glyph->bitmap_top  = bitmapglyph->top;
+    glyph->advance     = s->face->glyph->advance.x >> 6;
+
+    /* measure text height to calculate text_height (or the maximum text height) */
+    FT_Glyph_Get_CBox(glyph->glyph, ft_glyph_bbox_pixels, &glyph->bbox);
+
+    /* cache the newly created glyph */
+    if (!(node = av_tree_node_alloc())) {
+        ret = AVERROR(ENOMEM);
+        goto error;
+    }
+    av_tree_insert(&s->glyphs, glyph, glyph_cmp, &node);
+
+    if (glyph_ptr)
+        *glyph_ptr = glyph;
+    return 0;
+
+error:
+    if (glyph)
+        av_freep(&glyph->glyph);
+
+    av_freep(&glyph);
+    av_freep(&node);
+    return ret;
+}
+
+static av_cold int set_fontsize(AVFilterContext *ctx, unsigned int fontsize)
+{
+    int err;
+    hellosubsContext *s = ctx->priv;
+
+    if ((err = FT_Set_Pixel_Sizes(s->face, 0, fontsize))) {
+        av_log(ctx, AV_LOG_ERROR, "Could not set font size to %d pixels: %s\n",
+               fontsize, FT_ERRMSG(err));
+        return AVERROR(EINVAL);
+    }
+
+    s->fontsize = fontsize;
+
+    return 0;
+}
+
+static av_cold int parse_fontsize(AVFilterContext *ctx)
+{
+    hellosubsContext *s = ctx->priv;
+    int err;
+
+    if (s->fontsize_pexpr)
+        return 0;
+
+    if (s->fontsize_expr == NULL)
+        return AVERROR(EINVAL);
+
+    if ((err = av_expr_parse(&s->fontsize_pexpr, s->fontsize_expr, var_names,
+                             NULL, NULL, fun2_names, fun2, 0, ctx)) < 0)
+        return err;
+
+    return 0;
+}
+
+static av_cold int update_fontsize(AVFilterContext *ctx)
+{
+    hellosubsContext *s = ctx->priv;
+    unsigned int fontsize = s->default_fontsize;
+    int err;
+    double size, roundedsize;
+
+    // if no fontsize specified use the default
+    if (s->fontsize_expr != NULL) {
+        if ((err = parse_fontsize(ctx)) < 0)
+           return err;
+
+        size = av_expr_eval(s->fontsize_pexpr, s->var_values, &s->prng);
+
+        if (!isnan(size)) {
+            roundedsize = round(size);
+            // test for overflow before cast
+            if (!(roundedsize > INT_MIN && roundedsize < INT_MAX)) {
+                av_log(ctx, AV_LOG_ERROR, "fontsize overflow\n");
+                return AVERROR(EINVAL);
+            }
+
+            fontsize = roundedsize;
+        }
+    }
+
+    if (fontsize == 0)
+        fontsize = 1;
+
+    // no change
+    if (fontsize == s->fontsize)
+        return 0;
+
+    return set_fontsize(ctx, fontsize);
+}
+
+static int load_font_file(AVFilterContext *ctx, const char *path, int index)
+{
+    hellosubsContext *s = ctx->priv;
+    int err;
+
+    err = FT_New_Face(s->library, path, index, &s->face);
+    if (err) {
+#if !CONFIG_LIBFONTCONFIG
+        av_log(ctx, AV_LOG_ERROR, "Could not load font \"%s\": %s\n",
+               s->fontfile, FT_ERRMSG(err));
+#endif
+        return AVERROR(EINVAL);
+    }
+    return 0;
+}
+
+#if CONFIG_LIBFONTCONFIG
+static int load_font_fontconfig(AVFilterContext *ctx)
+{
+    hellosubsContext *s = ctx->priv;
+    FcConfig *fontconfig;
+    FcPattern *pat, *best;
+    FcResult result = FcResultMatch;
+    FcChar8 *filename;
+    int index;
+    double size;
+    int err = AVERROR(ENOENT);
+    int parse_err;
+
+    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);
+
+    parse_err = parse_fontsize(ctx);
+    if (!parse_err) {
+        double size = av_expr_eval(s->fontsize_pexpr, s->var_values, &s->prng);
+
+        if (isnan(size)) {
+            av_log(ctx, AV_LOG_ERROR, "impossible to find font information");
+            return AVERROR(EINVAL);
+        }
+
+        FcPatternAddDouble(pat, FC_SIZE, size);
+    }
+
+    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 (parse_err)
+        s->default_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)
+{
+    hellosubsContext *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 inline int is_newline(uint32_t c)
+{
+    return c == '\n' || c == '\r' || c == '\f' || c == '\v';
+}
+
+
+
+static av_cold int init(AVFilterContext *ctx)
+{
+    int err;
+    hellosubsContext *s = ctx->priv;
+    Glyph *glyph;
+
+    av_expr_free(s->fontsize_pexpr);
+    s->fontsize_pexpr = NULL;
+
+    s->fontsize = 0;
+    s->default_fontsize = 16;
+
+    if (!s->fontfile && !CONFIG_LIBFONTCONFIG) {
+        av_log(ctx, AV_LOG_ERROR, "No font filename provided\n");
+        return AVERROR(EINVAL);
+    }
+
+    
+
+    
+    if (s->tc_opt_string) {
+        int ret = av_timecode_init_from_string(&s->tc, s->tc_rate,
+                                               s->tc_opt_string, ctx);
+        if (ret < 0)
+            return ret;
+        if (s->tc24hmax)
+            s->tc.flags |= AV_TIMECODE_FLAG_24HOURSMAX;
+        if (!s->text)
+            s->text = av_strdup("");
+    }
+
+    if ((err = FT_Init_FreeType(&(s->library)))) {
+        av_log(ctx, AV_LOG_ERROR,
+               "Could not load FreeType: %s\n", FT_ERRMSG(err));
+        return AVERROR(EINVAL);
+    }
+
+    if ((err = load_font(ctx)) < 0)
+        return err;
+
+    if ((err = update_fontsize(ctx)) < 0)
+        return err;
+
+    if (s->borderw) {
+        if (FT_Stroker_New(s->library, &s->stroker)) {
+            av_log(ctx, AV_LOG_ERROR, "Coult not init FT stroker\n");
+            return AVERROR_EXTERNAL;
+        }
+        FT_Stroker_Set(s->stroker, s->borderw << 6, FT_STROKER_LINECAP_ROUND,
+                       FT_STROKER_LINEJOIN_ROUND, 0);
+    }
+
+    s->use_kerning = FT_HAS_KERNING(s->face);
+
+    /* load the fallback glyph with code 0 */
+    load_glyph(ctx, NULL, 0);
+
+    /* set the tabsize in pixels */
+    if ((err = load_glyph(ctx, &glyph, ' ')) < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Could not set tabsize.\n");
+        return err;
+    }
+    s->tabsize *= glyph->advance;
+
+    
+
+    av_bprint_init(&s->expanded_text, 0, AV_BPRINT_SIZE_UNLIMITED);
+    av_bprint_init(&s->expanded_fontcolor, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+    return 0;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    return ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0));
+}
+
+static int glyph_enu_free(void *opaque, void *elem)
+{
+    Glyph *glyph = elem;
+
+    FT_Done_Glyph(glyph->glyph);
+    FT_Done_Glyph(glyph->border_glyph);
+    av_free(elem);
+    return 0;
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    hellosubsContext *s = ctx->priv;
+
+    av_expr_free(s->x_pexpr);
+    av_expr_free(s->y_pexpr);
+    av_expr_free(s->a_pexpr);
+    av_expr_free(s->fontsize_pexpr);
+
+    s->x_pexpr = s->y_pexpr = s->a_pexpr = s->fontsize_pexpr = NULL;
+
+    av_freep(&s->positions);
+    s->nb_positions = 0;
+
+    av_tree_enumerate(s->glyphs, NULL, NULL, glyph_enu_free);
+    av_tree_destroy(s->glyphs);
+    s->glyphs = NULL;
+
+    FT_Done_Face(s->face);
+    FT_Stroker_Done(s->stroker);
+    FT_Done_FreeType(s->library);
+
+    av_bprint_finalize(&s->expanded_text, NULL);
+    av_bprint_finalize(&s->expanded_fontcolor, NULL);
+}
+
+static int config_input(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    hellosubsContext *s = ctx->priv;
+    int ret;
+
+    ff_draw_init(&s->dc, inlink->format, FF_DRAW_PROCESS_ALPHA);
+    ff_draw_color(&s->dc, &s->fontcolor,   s->fontcolor.rgba);
+    s->var_values[VAR_w]     = s->var_values[VAR_W]     = s->var_values[VAR_MAIN_W] = inlink->w;
+    s->var_values[VAR_h]     = s->var_values[VAR_H]     = s->var_values[VAR_MAIN_H] = inlink->h;
+    s->var_values[VAR_SAR]   = inlink->sample_aspect_ratio.num ? av_q2d(inlink->sample_aspect_ratio) : 1;
+    s->var_values[VAR_DAR]   = (double)inlink->w / inlink->h * s->var_values[VAR_SAR];
+    s->var_values[VAR_HSUB]  = 1 << s->dc.hsub_max;
+    s->var_values[VAR_VSUB]  = 1 << s->dc.vsub_max;
+    s->var_values[VAR_X]     = NAN;
+    s->var_values[VAR_Y]     = NAN;
+    s->var_values[VAR_T]     = NAN;
+
+    av_lfg_init(&s->prng, av_get_random_seed());
+
+    av_expr_free(s->x_pexpr);
+    av_expr_free(s->y_pexpr);
+    av_expr_free(s->a_pexpr);
+    s->x_pexpr = s->y_pexpr = s->a_pexpr = NULL;
+
+    if ((ret = av_expr_parse(&s->x_pexpr, s->x_expr, var_names,
+                             NULL, NULL, fun2_names, fun2, 0, ctx)) < 0 ||
+        (ret = av_expr_parse(&s->y_pexpr, s->y_expr, var_names,
+                             NULL, NULL, fun2_names, fun2, 0, ctx)) < 0 ||
+        (ret = av_expr_parse(&s->a_pexpr, s->a_expr, var_names,
+                             NULL, NULL, fun2_names, fun2, 0, ctx)) < 0)
+
+        return AVERROR(EINVAL);
+
+    return 0;
+}
+
+
+static int generatehellosub(AVFilterContext *ctx, AVBPrint *bp)
+                    
+{
+    hellosubsContext *s = ctx->priv;
+    double pts = s->var_values[VAR_T];
+    int64_t ms = llrint(pts * 1000);
+    
+    if (ms < 0) 
+       ms = -ms;
+    av_bprintf(bp, "Hello world %d:%02d",(int)(ms / (60 * 1000)),(int)(ms / 1000) % 60);
+    return 0;
+}
+
+
+static int draw_glyphs(hellosubsContext *s, AVFrame *frame,
+                       int width, int height,
+                       FFDrawColor *color,
+                       int x, int y, int borderw)
+{
+    char *text = s->expanded_text.str;
+    uint32_t code = 0;
+    int i, x1, y1;
+    uint8_t *p;
+    Glyph *glyph = NULL;
+
+    for (i = 0, p = text; *p; i++) {
+        FT_Bitmap bitmap;
+        Glyph dummy = { 0 };
+        GET_UTF8(code, *p++, continue;);
+
+        /* skip new line chars, just go to new line */
+        if (code == '\n' || code == '\r' || code == '\t')
+            continue;
+
+        dummy.code = code;
+        dummy.fontsize = s->fontsize;
+        glyph = av_tree_find(s->glyphs, &dummy, glyph_cmp, NULL);
+
+        bitmap = borderw ? glyph->border_bitmap : glyph->bitmap;
+
+        if (glyph->bitmap.pixel_mode != FT_PIXEL_MODE_MONO &&
+            glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
+            return AVERROR(EINVAL);
+
+        x1 = s->positions[i].x+s->x+x - borderw;
+        y1 = s->positions[i].y+s->y+y - borderw;
+
+        ff_blend_mask(&s->dc, color,
+                      frame->data, frame->linesize, width, height,
+                      bitmap.buffer, bitmap.pitch,
+                      bitmap.width, bitmap.rows,
+                      bitmap.pixel_mode == FT_PIXEL_MODE_MONO ? 0 : 3,
+                      0, x1, y1);
+    }
+
+    return 0;
+}
+
+
+static void update_color_with_alpha(hellosubsContext *s, FFDrawColor *color, const FFDrawColor incolor)
+{
+    *color = incolor;
+    color->rgba[3] = (color->rgba[3] * s->alpha) / 255;
+    ff_draw_color(&s->dc, color, color->rgba);
+}
+
+static void update_alpha(hellosubsContext *s)
+{
+    double alpha = av_expr_eval(s->a_pexpr, s->var_values, &s->prng);
+
+    if (isnan(alpha))
+        return;
+
+    if (alpha >= 1.0)
+        s->alpha = 255;
+    else if (alpha <= 0)
+        s->alpha = 0;
+    else
+        s->alpha = 256 * alpha;
+}
+
+static int draw_text(AVFilterContext *ctx, AVFrame *frame,
+                     int width, int height)
+{
+    hellosubsContext *s = ctx->priv;
+    uint32_t code = 0, prev_code = 0;
+    int x = 0, y = 0, i = 0, ret;
+    int max_text_line_w = 0, len;
+    
+    char *text;
+    uint8_t *p;
+    int y_min = 32000, y_max = -32000;
+    int x_min = 32000, x_max = -32000;
+    FT_Vector delta;
+    Glyph *glyph = NULL, *prev_glyph = NULL;
+    Glyph dummy = { 0 };
+
+    AVBPrint *bp = &s->expanded_text;
+
+    FFDrawColor fontcolor;
+    av_bprint_clear(bp);
+
+    if ((ret = generatehellosub(ctx,&s->expanded_text)) < 0)
+         return ret;
+    text = s->expanded_text.str;
+    len=20;
+    if (len > s->nb_positions) {
+        if (!(s->positions =
+              av_realloc(s->positions, len*sizeof(*s->positions))))
+            return AVERROR(ENOMEM);
+        s->nb_positions = len;
+
+    }
+
+    x = 0;
+    y = 0;
+
+    if ((ret = update_fontsize(ctx)) < 0)
+        return ret;
+
+    /* load and cache glyphs */
+    for (i = 0, p = text; *p; i++) {
+        GET_UTF8(code, *p++, continue;);
+
+        /* get glyph */
+        dummy.code = code;
+        dummy.fontsize = s->fontsize;
+        glyph = av_tree_find(s->glyphs, &dummy, glyph_cmp, NULL);
+        if (!glyph) {
+            ret = load_glyph(ctx, &glyph, code);
+            if (ret < 0)
+                return ret;
+        }
+
+        y_min = FFMIN(glyph->bbox.yMin, y_min);
+        y_max = FFMAX(glyph->bbox.yMax, y_max);
+        x_min = FFMIN(glyph->bbox.xMin, x_min);
+        x_max = FFMAX(glyph->bbox.xMax, x_max);
+    }
+    s->max_glyph_h = y_max - y_min;
+    s->max_glyph_w = x_max - x_min;
+
+    /* compute and save position for each glyph */
+    glyph = NULL;
+    for (i = 0, p = text; *p; i++) {
+        GET_UTF8(code, *p++, continue;);
+
+        /* skip the \n in the sequence \r\n */
+        if (prev_code == '\r' && code == '\n')
+            continue;
+
+        prev_code = code;
+        if (is_newline(code)) {
+
+            max_text_line_w = FFMAX(max_text_line_w, x);
+            y += s->max_glyph_h + s->line_spacing;
+            x = 0;
+            continue;
+        }
+
+        /* get glyph */
+        prev_glyph = glyph;
+        dummy.code = code;
+        dummy.fontsize = s->fontsize;
+        glyph = av_tree_find(s->glyphs, &dummy, glyph_cmp, NULL);
+
+        /* kerning */
+        if (s->use_kerning && prev_glyph && glyph->code) {
+            FT_Get_Kerning(s->face, prev_glyph->code, glyph->code,
+                           ft_kerning_default, &delta);
+            x += delta.x >> 6;
+        }
+
+        /* save position */
+        s->positions[i].x = x + glyph->bitmap_left;
+        s->positions[i].y = y - glyph->bitmap_top + y_max;
+        if (code == '\t') x  = (x / s->tabsize + 1)*s->tabsize;
+        else              x += glyph->advance;
+    }
+
+    max_text_line_w = FFMAX(x, max_text_line_w);
+
+    s->var_values[VAR_TW] = s->var_values[VAR_TEXT_W] = max_text_line_w;
+    s->var_values[VAR_TH] = s->var_values[VAR_TEXT_H] = y + s->max_glyph_h;
+
+    s->var_values[VAR_MAX_GLYPH_W] = s->max_glyph_w;
+    s->var_values[VAR_MAX_GLYPH_H] = s->max_glyph_h;
+    s->var_values[VAR_MAX_GLYPH_A] = s->var_values[VAR_ASCENT ] = y_max;
+    s->var_values[VAR_MAX_GLYPH_D] = s->var_values[VAR_DESCENT] = y_min;
+
+    s->var_values[VAR_LINE_H] = s->var_values[VAR_LH] = s->max_glyph_h;
+
+    s->x = s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, &s->prng);
+    s->y = s->var_values[VAR_Y] = av_expr_eval(s->y_pexpr, s->var_values, &s->prng);
+    /* It is necessary if x is expressed from y  */
+    s->x = s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, &s->prng);
+
+    update_alpha(s);
+    update_color_with_alpha(s, &fontcolor  , s->fontcolor  );
+    
+    if (s->fix_bounds) {
+
+        /* calculate footprint of text effects */
+        
+
+        int offsetleft = FFMAX3(0,0,0);
+                                
+        int offsettop = FFMAX3(0, 0,0);
+                                
+        if (s->x - offsetleft < 0) s->x = offsetleft;
+        if (s->y - offsettop < 0)  s->y = offsettop;
+
+        
+    }
+
+    
+    
+    if ((ret = draw_glyphs(s, frame, width, height,
+                           &fontcolor, 0, 0, 0)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    AVFilterLink *outlink = ctx->outputs[0];
+    hellosubsContext *s = ctx->priv;
+    
+    s->var_values[VAR_N] = inlink->frame_count_out + s->start_number;
+    s->var_values[VAR_T] = frame->pts == AV_NOPTS_VALUE ?
+        NAN : frame->pts * av_q2d(inlink->time_base);
+
+    s->var_values[VAR_PICT_TYPE] = frame->pict_type;
+    s->metadata = frame->metadata;
+
+    draw_text(ctx, frame, frame->width, frame->height);
+
+    av_log(ctx, AV_LOG_DEBUG, "n:%d t:%f text_w:%d text_h:%d x:%d y:%d\n",
+           (int)s->var_values[VAR_N], s->var_values[VAR_T],
+           (int)s->var_values[VAR_TEXT_W], (int)s->var_values[VAR_TEXT_H],
+           s->x, s->y);
+
+    return ff_filter_frame(outlink, frame);
+}
+
+static const AVFilterPad avfilter_vf_hellosubs_inputs[] = {
+    {
+        .name           = "default",
+        .type           = AVMEDIA_TYPE_VIDEO,
+        .filter_frame   = filter_frame,
+        .config_props   = config_input,
+        .needs_writable = 1,
+    },
+    { NULL }
+};
+
+static const AVFilterPad avfilter_vf_hellosubs_outputs[] = {
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_VIDEO,
+    },
+    { NULL }
+};
+
+AVFilter ff_vf_hellosubs = {
+    .name          = "hellosubs",
+    .description   = NULL_IF_CONFIG_SMALL("Writes hello world time on top of video frames using libfreetype library."),
+    .priv_size     = sizeof(hellosubsContext),
+    .priv_class    = &hellosubs_class,
+    .init          = init,
+    .uninit        = uninit,
+    .query_formats = query_formats,
+    .inputs        = avfilter_vf_hellosubs_inputs,
+    .outputs       = avfilter_vf_hellosubs_outputs,
+    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+};
-- 
2.7.4

