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
@@ -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"
@@ -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
@@ -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;
new file mode 100644
@@ -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