[FFmpeg-devel] GSOC 2018 qualification task.
ANURAG SINGH IIT BHU
anurag.singh.phy15 at iitbhu.ac.in
Mon Apr 9 05:59:21 EEST 2018
This mail is regarding the qualification task assigned to me for the
GSOC project
in FFmpeg for automatic real-time subtitle generation using speech to text
translation ML model. My assigned task by Michael sir was writing a
ffmpeg-libavfilter filter which outputs a "Hello World minute: sec"
subtitle each second.
I have built a libavfilter filter named "hellosubs" using the existing
subtitle filter. hellosubs filter accepts a video file as input, along with
any subtitle file of any supported subtitle format of FFmpeg with any
number of enteries(>0), any entries i.e any random subtitle file, as an
argument and writes "Hello World minute: sec" subtitle each second on the
video.
>From 38fcf8c80f71a4186f03f33c9272b707390add67 Mon Sep 17 00:00:00 2001
From: ddosvulnerability <anurag.singh.phy15 at iitbhu.ac.in>
Date: Fri, 6 Apr 2018 11:30:17 +0530
Subject: [PATCH] avfilter: add hellosub filter.
---
libavfilter/Makefile | 1 +
libavfilter/allfilters.c | 1 +
libavfilter/vf_hellosubs.c | 463
+++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 465 insertions(+)
create mode 100644 libavfilter/vf_hellosubs.c
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index a90ca30..770b1b5 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -331,6 +331,7 @@ OBJS-$(CONFIG_SSIM_FILTER) +=
vf_ssim.o framesync.o
OBJS-$(CONFIG_STEREO3D_FILTER) += vf_stereo3d.o
OBJS-$(CONFIG_STREAMSELECT_FILTER) += f_streamselect.o
framesync.o
OBJS-$(CONFIG_SUBTITLES_FILTER) += vf_subtitles.o
+OBJS-$(CONFIG_HELLOSUBS_FILTER) += vf_hellosubs.o
OBJS-$(CONFIG_SUPER2XSAI_FILTER) += vf_super2xsai.o
OBJS-$(CONFIG_SWAPRECT_FILTER) += vf_swaprect.o
OBJS-$(CONFIG_SWAPUV_FILTER) += vf_swapuv.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 6eac828..a008908 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -322,6 +322,7 @@ extern AVFilter ff_vf_ssim;
extern AVFilter ff_vf_stereo3d;
extern AVFilter ff_vf_streamselect;
extern AVFilter ff_vf_subtitles;
+extern AVFilter ff_vf_hellosubs;
extern AVFilter ff_vf_super2xsai;
extern AVFilter ff_vf_swaprect;
extern AVFilter ff_vf_swapuv;
diff --git a/libavfilter/vf_hellosubs.c b/libavfilter/vf_hellosubs.c
new file mode 100644
index 0000000..7ba3a0e
--- /dev/null
+++ b/libavfilter/vf_hellosubs.c
@@ -0,0 +1,463 @@
+/*
+ * Copyright (c) 2011 Baptiste Coudurier
+ * Copyright (c) 2011 Stefano Sabatini
+ * Copyright (c) 2012 Clément Bœsch
+ *
+ * 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
+ * Libass hellosubs burning filter.
+ *
+ * @see{http://www.matroska.org/technical/specs/hellosubs/ssa.html}
+ */
+
+#include <ass/ass.h>
+
+#include "config.h"
+#if CONFIG_SUBTITLES_FILTER
+# include "libavcodec/avcodec.h"
+# include "libavformat/avformat.h"
+#endif
+#include "libavutil/avstring.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/opt.h"
+#include "libavutil/parseutils.h"
+#include "drawutils.h"
+#include "avfilter.h"
+#include "internal.h"
+#include "formats.h"
+#include "video.h"
+#include <stdio.h>
+
+typedef struct AssContext {
+ const AVClass *class;
+ ASS_Library *library;
+ ASS_Renderer *renderer;
+ ASS_Track *track;
+ char *filename;
+ char *fontsdir;
+ char *charenc;
+ char *force_style;
+ int stream_index;
+ int alpha;
+ uint8_t rgba_map[4];
+ int pix_step[4]; ///< steps per pixel for each plane of the
main output
+ int original_w, original_h;
+ int shaping;
+ FFDrawContext draw;
+} AssContext;
+
+#define OFFSET(x) offsetof(AssContext, x)
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+
+#define COMMON_OPTIONS \
+ {"filename", "set the filename of file to
read", OFFSET(filename), AV_OPT_TYPE_STRING,
{.str = NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, \
+ {"f", "set the filename of file to
read", OFFSET(filename), AV_OPT_TYPE_STRING,
{.str = NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, \
+ {"original_size", "set the size of the original video (used to scale
fonts)", OFFSET(original_w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL},
CHAR_MIN, CHAR_MAX, FLAGS }, \
+ {"fontsdir", "set the directory containing the fonts to
read", OFFSET(fontsdir), AV_OPT_TYPE_STRING, {.str =
NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, \
+ {"alpha", "enable processing of alpha
channel", OFFSET(alpha), AV_OPT_TYPE_BOOL,
{.i64 = 0 }, 0, 1, FLAGS }, \
+
+/* libass supports a log level ranging from 0 to 7 */
+static const int ass_libavfilter_log_level_map[] = {
+ [0] = AV_LOG_FATAL, /* MSGL_FATAL */
+ [1] = AV_LOG_ERROR, /* MSGL_ERR */
+ [2] = AV_LOG_WARNING, /* MSGL_WARN */
+ [3] = AV_LOG_WARNING, /* <undefined> */
+ [4] = AV_LOG_INFO, /* MSGL_INFO */
+ [5] = AV_LOG_INFO, /* <undefined> */
+ [6] = AV_LOG_VERBOSE, /* MSGL_V */
+ [7] = AV_LOG_DEBUG, /* MSGL_DBG2 */
+};
+
+static void ass_log(int ass_level, const char *fmt, va_list args, void
*ctx)
+{
+ const int ass_level_clip = av_clip(ass_level, 0,
+ FF_ARRAY_ELEMS(ass_libavfilter_log_level_map) - 1);
+ const int level = ass_libavfilter_log_level_map[ass_level_clip];
+
+ av_vlog(ctx, level, fmt, args);
+ av_log(ctx, level, "\n");
+}
+
+static av_cold int init(AVFilterContext *ctx)
+{
+ AssContext *ass = ctx->priv;
+
+ if (!ass->filename) {
+ av_log(ctx, AV_LOG_ERROR, "No filename provided!\n");
+ return AVERROR(EINVAL);
+ }
+
+ ass->library = ass_library_init();
+ if (!ass->library) {
+ av_log(ctx, AV_LOG_ERROR, "Could not initialize libass.\n");
+ return AVERROR(EINVAL);
+ }
+ ass_set_message_cb(ass->library, ass_log, ctx);
+
+ ass_set_fonts_dir(ass->library, ass->fontsdir);
+
+ ass->renderer = ass_renderer_init(ass->library);
+ if (!ass->renderer) {
+ av_log(ctx, AV_LOG_ERROR, "Could not initialize libass
renderer.\n");
+ return AVERROR(EINVAL);
+ }
+
+ return 0;
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+ AssContext *ass = ctx->priv;
+
+ if (ass->track)
+ ass_free_track(ass->track);
+ if (ass->renderer)
+ ass_renderer_done(ass->renderer);
+ if (ass->library)
+ ass_library_done(ass->library);
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+ return ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0));
+}
+
+static int config_input(AVFilterLink *inlink)
+{
+ AssContext *ass = inlink->dst->priv;
+
+ ff_draw_init(&ass->draw, inlink->format, ass->alpha ?
FF_DRAW_PROCESS_ALPHA : 0);
+
+ ass_set_frame_size (ass->renderer, inlink->w, inlink->h);
+ if (ass->original_w && ass->original_h)
+ ass_set_aspect_ratio(ass->renderer, (double)inlink->w / inlink->h,
+ (double)ass->original_w / ass->original_h);
+ if (ass->shaping != -1)
+ ass_set_shaper(ass->renderer, ass->shaping);
+
+ return 0;
+}
+
+/* libass stores an RGBA color in the format RRGGBBTT, where TT is the
transparency level */
+#define AR(c) ( (c)>>24)
+#define AG(c) (((c)>>16)&0xFF)
+#define AB(c) (((c)>>8) &0xFF)
+#define AA(c) ((0xFF-(c)) &0xFF)
+
+static void overlay_ass_image(AssContext *ass, AVFrame *picref,
+ const ASS_Image *image)
+{
+ for (; image; image = image->next) {
+ uint8_t rgba_color[] = {AR(image->color), AG(image->color),
AB(image->color), AA(image->color)};
+ FFDrawColor color;
+ ff_draw_color(&ass->draw, &color, rgba_color);
+ ff_blend_mask(&ass->draw, &color,
+ picref->data, picref->linesize,
+ picref->width, picref->height,
+ image->bitmap, image->stride, image->w, image->h,
+ 3, 0, image->dst_x, image->dst_y);
+ }
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *picref)
+{
+ AVFilterContext *ctx = inlink->dst;
+ AVFilterLink *outlink = ctx->outputs[0];
+ AssContext *ass = ctx->priv;
+ int detect_change = 0;
+ double time_ms = picref->pts * av_q2d(inlink->time_base) * 1000;
+ ASS_Image *image = ass_render_frame(ass->renderer, ass->track,
+ time_ms, &detect_change);
+
+ if (detect_change)
+ av_log(ctx, AV_LOG_DEBUG, "Change happened at time ms:%f\n",
time_ms);
+
+ overlay_ass_image(ass, picref, image);
+
+ return ff_filter_frame(outlink, picref);
+}
+
+static const AVFilterPad ass_inputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .filter_frame = filter_frame,
+ .config_props = config_input,
+ .needs_writable = 1,
+ },
+ { NULL }
+};
+
+static const AVFilterPad ass_outputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ },
+ { NULL }
+};
+
+
+
+#if CONFIG_HELLOSUBS_FILTER
+
+static const AVOption hellosubs_options[] = {
+ COMMON_OPTIONS
+ {"charenc", "set input character encoding", OFFSET(charenc),
AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, FLAGS},
+ {"stream_index", "set stream index", OFFSET(stream_index),
AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS},
+ {"si", "set stream index", OFFSET(stream_index),
AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS},
+ {"force_style", "force subtitle style", OFFSET(force_style),
AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, FLAGS},
+ {NULL},
+};
+
+static const char * const font_mimetypes[] = {
+ "application/x-truetype-font",
+ "application/vnd.ms-opentype",
+ "application/x-font-ttf",
+ NULL
+};
+
+static int attachment_is_font(AVStream * st)
+{
+ const AVDictionaryEntry *tag = NULL;
+ int n;
+
+ tag = av_dict_get(st->metadata, "mimetype", NULL, AV_DICT_MATCH_CASE);
+
+ if (tag) {
+ for (n = 0; font_mimetypes[n]; n++) {
+ if (av_strcasecmp(font_mimetypes[n], tag->value) == 0)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+AVFILTER_DEFINE_CLASS(hellosubs);
+
+static av_cold int init_hellosubs(AVFilterContext *ctx)
+{
+ int j, ret, sid;long int z=0;int t1=0;
+ int k = 0;
+ AVDictionary *codec_opts = NULL;
+ AVFormatContext *fmt = NULL;
+ AVCodecContext *dec_ctx = NULL;
+ AVCodec *dec = NULL;
+ const AVCodecDescriptor *dec_desc;
+ AVStream *st;
+ AVPacket pkt;
+ AssContext *ass = ctx->priv;
+
+ /* Init libass */
+ ret = init(ctx);
+ if (ret < 0)
+ return ret;
+ ass->track = ass_new_track(ass->library);
+ if (!ass->track) {
+ av_log(ctx, AV_LOG_ERROR, "Could not create a libass track\n");
+ return AVERROR(EINVAL);
+ }
+
+ /* Open hellosubs file */
+ ret = avformat_open_input(&fmt, ass->filename, NULL, NULL);
+ if (ret < 0) {
+ av_log(ctx, AV_LOG_ERROR, "Unable to open %s\n", ass->filename);
+
+ }
+
+
+ /* Locate hellosubs stream */
+ if (ass->stream_index < 0)
+ ret = av_find_best_stream(fmt, AVMEDIA_TYPE_SUBTITLE, -1, -1,
NULL, 0);
+ else {
+ ret = -1;
+ if (ass->stream_index < fmt->nb_streams) {
+ for (j = 0; j < fmt->nb_streams; j++) {
+ if (fmt->streams[j]->codecpar->codec_type ==
AVMEDIA_TYPE_SUBTITLE) {
+ if (ass->stream_index == k) {
+ ret = j;
+ break;
+ }
+ k++;
+ }
+ }
+ }
+ }
+
+
+ sid = ret;
+ st = fmt->streams[sid];
+
+ /* Load attached fonts */
+ for (j = 0; j < fmt->nb_streams; j++) {
+ AVStream *st = fmt->streams[j];
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_ATTACHMENT &&
+ attachment_is_font(st)) {
+ const AVDictionaryEntry *tag = NULL;
+ tag = av_dict_get(st->metadata, "filename", NULL,
+ AV_DICT_MATCH_CASE);
+
+ if (tag) {
+ av_log(ctx, AV_LOG_DEBUG, "Loading attached font: %s\n",
+ tag->value);
+ ass_add_font(ass->library, tag->value,
+ st->codecpar->extradata,
+ st->codecpar->extradata_size);
+ } else {
+ av_log(ctx, AV_LOG_WARNING,
+ "Font attachment has no filename, ignored.\n");
+ }
+ }
+ }
+
+ /* Initialize fonts */
+ ass_set_fonts(ass->renderer, NULL, NULL, 1, NULL, 1);
+
+ /* Open decoder */
+ dec = avcodec_find_decoder(st->codecpar->codec_id);
+ if (!dec) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to find subtitle codec %s\n",
+ avcodec_get_name(st->codecpar->codec_id));
+ return AVERROR(EINVAL);
+ }
+ dec_desc = avcodec_descriptor_get(st->codecpar->codec_id);
+ if (dec_desc && !(dec_desc->props & AV_CODEC_PROP_TEXT_SUB)) {
+ av_log(ctx, AV_LOG_ERROR,
+ "Only text based hellosubs are currently supported\n");
+ return AVERROR_PATCHWELCOME;
+ }
+ if (ass->charenc)
+ av_dict_set(&codec_opts, "sub_charenc", ass->charenc, 0);
+ if (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57,26,100))
+ av_dict_set(&codec_opts, "sub_text_format", "ass", 0);
+
+ dec_ctx = avcodec_alloc_context3(dec);
+ if (!dec_ctx)
+ return AVERROR(ENOMEM);
+
+ ret = avcodec_parameters_to_context(dec_ctx, st->codecpar);
+ if (ret < 0)
+ goto end;
+
+ /*
+ * This is required by the decoding process in order to rescale the
+ * timestamps: in the current API the decoded hellosubs have their pts
+ * expressed in AV_TIME_BASE, and thus the lavc internals need to know
the
+ * stream time base in order to achieve the rescaling.
+ *
+ * That API is old and needs to be reworked to match behaviour with
A/V.
+ */
+ dec_ctx->pkt_timebase = st->time_base;
+
+ ret = avcodec_open2(dec_ctx, NULL, &codec_opts);
+ //if (ret < 0)
+ //goto end;
+
+ if (ass->force_style) {
+ char **list = NULL;
+ char *temp = NULL;
+ char *ptr = av_strtok(ass->force_style, ",", &temp);
+ int i = 0;
+ while (ptr) {
+ av_dynarray_add(&list, &i, ptr);
+ if (!list) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+ ptr = av_strtok(NULL, ",", &temp);
+ }
+ av_dynarray_add(&list, &i, NULL);
+ if (!list) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+ ass_set_style_overrides(ass->library, list);
+ av_free(list);
+ }
+ /* Decode hellosubs and push them into the renderer (libass) */
+ if (dec_ctx->subtitle_header)
+ ass_process_codec_private(ass->track,
+ dec_ctx->subtitle_header,
+ dec_ctx->subtitle_header_size);
+ av_init_packet(&pkt);
+ pkt.data = NULL;
+ pkt.size = 0;
+AVSubtitle sub = {0};
+int got_subtitle;
+
+
+ while (z<99999) {
+
+ {int e = avcodec_decode_subtitle2(dec_ctx, &sub, &got_subtitle, &pkt);}
+ got_subtitle=1;
+ ret=1;
+ {
+
+
+ {
+ int64_t start_time;
+ int64_t duration;
+ start_time=t1;duration=1000;
+ {
+
+
+
+ char a[100];char am[100];char am1[100];char ass_line1[100];
+ sprintf(a, "%ld",z);
+ sprintf(am, "%d",t1/60000);
+ sprintf(am1, "%d",(t1/1000)%60);
+
+ strcat(a,",0,Default,,0,0,0,,Hello world ");
+ strcat(a,am);
+ strcat(a,":");
+ strcat(a,am1);
+ strcpy(ass_line1, a);
+
+
+ {ass_process_chunk(ass->track, ass_line1,
strlen(ass_line1),
+ start_time,
duration);z++;t1=t1+1000;
+
+
+}
+ }
+ }
+ }
+
+ }
+
+end:
+ av_dict_free(&codec_opts);
+ avcodec_close(dec_ctx);
+ avcodec_free_context(&dec_ctx);
+ avformat_close_input(&fmt);
+ return ret;
+}
+
+AVFilter ff_vf_hellosubs = {
+ .name = "hellosubs",
+ .description = NULL_IF_CONFIG_SMALL("Render text hello world time
subtitle onto input video using the libass library."),
+ .priv_size = sizeof(AssContext),
+ .init = init_hellosubs,
+ .uninit = uninit,
+ .query_formats = query_formats,
+ .inputs = ass_inputs,
+ .outputs = ass_outputs,
+ .priv_class = &hellosubs_class,
+};
+#endif
--
2.7.4
Thanks and regards,
Anurag Singh.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-avfilter-add-hellosub-filter.patch
Type: text/x-patch
Size: 16931 bytes
Desc: not available
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20180409/97971de7/attachment.bin>
More information about the ffmpeg-devel
mailing list