diff mbox series

[FFmpeg-devel,v4,16/18] avfilter/stripstyles: Add stripstyles filter

Message ID MN2PR04MB59817317C179504249AE8302BAD79@MN2PR04MB5981.namprd04.prod.outlook.com
State Superseded, archived
Headers show
Series None | expand

Commit Message

Soft Works Sept. 11, 2021, 8:40 a.m. UTC
Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavfilter/Makefile         |   1 +
 libavfilter/allfilters.c     |   1 +
 libavfilter/sf_stripstyles.c | 246 +++++++++++++++++++++++++++++++++++
 3 files changed, 248 insertions(+)
 create mode 100644 libavfilter/sf_stripstyles.c

Comments

Paul B Mahol Sept. 11, 2021, 10:38 a.m. UTC | #1
On Sat, Sep 11, 2021 at 10:40 AM Soft Works <softworkz@hotmail.com> wrote:

> Signed-off-by: softworkz <softworkz@hotmail.com>
> ---
>  libavfilter/Makefile         |   1 +
>  libavfilter/allfilters.c     |   1 +
>  libavfilter/sf_stripstyles.c | 246 +++++++++++++++++++++++++++++++++++
>  3 files changed, 248 insertions(+)
>  create mode 100644 libavfilter/sf_stripstyles.c
>
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index 5a5a4be47e..626e1bed74 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -536,6 +536,7 @@ OBJS-$(CONFIG_NULLSINK_FILTER)               +=
> vsink_nullsink.o
>
>  # subtitle filters
>  OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
> +OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
>
>  # multimedia filters
>  OBJS-$(CONFIG_ABITSCOPE_FILTER)              += avf_abitscope.o
> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> index 6d7a535ee8..8f7d697408 100644
> --- a/libavfilter/allfilters.c
> +++ b/libavfilter/allfilters.c
> @@ -524,6 +524,7 @@ extern const AVFilter ff_avf_showvolume;
>  extern const AVFilter ff_avf_showwaves;
>  extern const AVFilter ff_avf_showwavespic;
>  extern const AVFilter ff_vaf_spectrumsynth;
> +extern const AVFilter ff_sf_stripstyles;
>  extern const AVFilter ff_sf_textmod;
>  extern const AVFilter ff_svf_graphicsub2video;
>  extern const AVFilter ff_svf_textsub2video;
> diff --git a/libavfilter/sf_stripstyles.c b/libavfilter/sf_stripstyles.c
> new file mode 100644
> index 0000000000..de2be0b1a2
> --- /dev/null
> +++ b/libavfilter/sf_stripstyles.c
> @@ -0,0 +1,246 @@
> +/*
> + * Copyright (c) 2021 softworkz
> + *
> + * 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
> + * text subtitle filter which removes inline-styles from subtitles
> + */
> +
> +#include <libavcodec/ass.h>
> +
> +#include "libavutil/avassert.h"
> +#include "libavutil/opt.h"
> +#include "avfilter.h"
> +#include "internal.h"
> +#include "libavcodec/avcodec.h"
> +#include "libavcodec/ass_split.h"
> +
> +typedef struct StripStylesContext {
> +    const AVClass *class;
> +    enum AVSubtitleType format;
> +    int remove_animated;
> +} StripStylesContext;
> +
> +typedef struct DialogContext {
> +    const AVClass *class;
> +    const StripStylesContext* ss_ctx;
> +    AVBPrint buffer;
> +    int drawing_scale;
> +    int is_animated;
> +} DialogContext;
> +
> +static void dialog_text_cb(void *priv, const char *text, int len)
> +{
> +    DialogContext *s = priv;
> +    if (!s->drawing_scale && (!s->is_animated ||
> !s->ss_ctx->remove_animated))
> +        av_bprint_append_data(&s->buffer, text, len);
> +}
> +
> +static void dialog_new_line_cb(void *priv, int forced)
> +{
> +    DialogContext *s = priv;
> +    if (!s->drawing_scale && !s->is_animated)
> +        av_bprint_append_data(&s->buffer, forced ? "\\N" : "\\n", 2);
> +}
> +
> +static void dialog_drawing_mode_cb(void *priv, int scale)
> +{
> +    DialogContext *s = priv;
> +    s->drawing_scale = scale;
> +}
> +
> +static void dialog_animate_cb(void *priv, int t1, int t2, int accel, char
> *style)
> +{
> +    DialogContext *s = priv;
> +    s->is_animated = 1;
> +}
> +
> +static void dialog_move_cb(void *priv, int x1, int y1, int x2, int y2,
> int t1, int t2)
> +{
> +    DialogContext *s = priv;
> +    if (t1 >= 0 || t2 >= 0)
> +        s->is_animated = 1;
> +}
> +
> +static const ASSCodesCallbacks dialog_callbacks = {
> +    .text             = dialog_text_cb,
> +    .new_line         = dialog_new_line_cb,
> +    .drawing_mode     = dialog_drawing_mode_cb,
> +    .animate          = dialog_animate_cb,
> +    .move             = dialog_move_cb,
> +};
> +
> +static int query_formats(AVFilterContext *ctx)
> +{
> +    AVFilterFormats *formats;
> +    AVFilterLink *inlink = ctx->inputs[0];
> +    AVFilterLink *outlink = ctx->outputs[0];
> +    static const enum AVSubtitleType subtitle_fmts[] = {
> AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE };
> +    int ret;
> +
> +    /* set input subtitle format */
> +    formats = ff_make_format_list(subtitle_fmts);
> +    if ((ret = ff_formats_ref(formats, &inlink->outcfg.formats)) < 0)
> +        return ret;
> +
> +    /* set output video format */
> +    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
> +        return ret;
> +
> +    return 0;
> +}
> +
> +static char *ass_get_line(int readorder, int layer, const char *style,
> +                        const char *speaker, const char *effect, const
> char *text)
> +{
> +    return av_asprintf("%d,%d,%s,%s,0,0,0,%s,%s",
> +                       readorder, layer, style ? style : "Default",
> +                       speaker ? speaker : "", effect, text);
> +}
> +
> +static char *process_dialog(StripStylesContext *s, const char *ass_line)
> +{
> +    ASSDialog *dialog = ff_ass_split_dialog(NULL, ass_line);
> +    char *result = NULL, *text;
> +    DialogContext dlg_ctx = { 0 };
> +
> +    if (!dialog)
> +        return NULL;
> +
> +    dlg_ctx.ss_ctx = s;
> +
> +    av_bprint_init(&dlg_ctx.buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
> +
> +    ff_ass_split_override_codes(&dialog_callbacks, &dlg_ctx,
> dialog->text);
> +
> +    av_bprint_finalize(&dlg_ctx.buffer, &text);
> +
> +    if (text && strlen(text) > 0)
> +        result = ass_get_line(dialog->readorder, dialog->layer,
> dialog->style, dialog->name, dialog->effect, text);
> +
> +    av_free(text);
> +    ff_ass_free_dialog(&dialog);
> +
> +    return result;
> +}
> +
> +static int filter_frame(AVFilterLink *inlink, AVFrame *src_frame)
> +{
> +    StripStylesContext *s = inlink->dst->priv;
> +    AVFilterLink *outlink = inlink->dst->outputs[0];
> +    AVSubtitle *sub;
> +    int ret;
> +    AVFrame *out;
> +
> +    outlink->format = inlink->format;
> +
> +    out = av_frame_alloc();
> +    if (!out) {
> +        av_frame_free(&src_frame);
> +        return AVERROR(ENOMEM);
> +    }
> +
> +    out->format = outlink->format;
> +
> +    if ((ret = av_frame_get_buffer2(out, AVMEDIA_TYPE_SUBTITLE, 0)) < 0)
>

Shouldn't this use implementation of ff_get_subtitle_buffer()?

+        return ret;
> +
> +    out->pts                    = src_frame->pts;
> +    out->repeat_pict            = src_frame->repeat_pict;
> +    out->pkt_dts                = src_frame->pkt_dts;
> +    out->pkt_pos                = src_frame->pkt_pos;
> +    out->pkt_size               = src_frame->pkt_size;
> +    out->pkt_duration           = src_frame->pkt_duration;
> +    out->reordered_opaque       = src_frame->reordered_opaque;
> +    out->best_effort_timestamp  = src_frame->best_effort_timestamp;
> +    out->flags                  = src_frame->flags;
> +
> +    sub = (AVSubtitle *)src_frame->data[0];
> +
> +    if (sub) {
> +        AVSubtitle *out_sub = av_memdup(sub, sizeof(*out_sub));
> +        if (!out_sub)
> +            return AVERROR(ENOMEM);
> +
> +        out->buf[0] = av_buffer_create((uint8_t*)out_sub,
> sizeof(*out_sub), avsubtitle_free_ref, NULL, AV_BUFFER_FLAG_READONLY);
> +        out->data[0] = (uint8_t*)out_sub;
> +
> +        if (sub->num_rects) {
> +            out_sub->rects = av_malloc_array(sub->num_rects,
> sizeof(AVSubtitleRect *));
> +        }
> +
> +        for (unsigned i = 0; i < sub->num_rects; i++) {
> +
> +            AVSubtitleRect *src_rect = sub->rects[i];
> +            AVSubtitleRect *dst_rect = av_memdup(src_rect,
> sizeof(*dst_rect));
> +            out_sub->rects[i] = dst_rect;
> +
> +            if (src_rect->ass) {
> +                dst_rect->ass = process_dialog(s, src_rect->ass);
> +
> +                if (dst_rect->ass) {
> +                    av_log(inlink->dst, AV_LOG_DEBUG, "original: %s\n",
> src_rect->ass);
> +                    av_log(inlink->dst, AV_LOG_DEBUG, "stripped: %s\n",
> dst_rect->ass);
> +                }
> +                else
> +                    dst_rect->ass = av_strdup("");
> +            }
> +        }
> +    }
> +
> +    av_frame_free(&src_frame);
> +    return ff_filter_frame(outlink, out);
> +}
> +
> +#define OFFSET(x) offsetof(StripStylesContext, x)
> +#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
> +
> +static const AVOption stripstyles_options[] = {
> +    { "remove_animated", "remove animated text (default: yes)",
>  OFFSET(remove_animated),  AV_OPT_TYPE_BOOL, {.i64 = 1 }, 0, 1, FLAGS, 0 },
> +    { NULL },
> +};
> +
> +AVFILTER_DEFINE_CLASS(stripstyles);
> +
> +static const AVFilterPad inputs[] = {
> +    {
> +        .name         = "default",
> +        .type         = AVMEDIA_TYPE_SUBTITLE,
> +        .filter_frame = filter_frame,
> +    },
> +};
> +
> +static const AVFilterPad outputs[] = {
> +    {
> +        .name          = "default",
> +        .type          = AVMEDIA_TYPE_SUBTITLE,
> +    },
> +};
> +
> +const AVFilter ff_sf_stripstyles = {
> +    .name          = "stripstyles",
> +    .description   = NULL_IF_CONFIG_SMALL("Strip subtitle inline styles"),
> +    .query_formats = query_formats,
> +    .priv_size     = sizeof(StripStylesContext),
> +    .priv_class    = &stripstyles_class,
> +    FILTER_INPUTS(inputs),
> +    FILTER_OUTPUTS(outputs),
> +};
> +
> --
> 2.30.2.windows.1
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
diff mbox series

Patch

diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 5a5a4be47e..626e1bed74 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -536,6 +536,7 @@  OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
 
 # subtitle filters
 OBJS-$(CONFIG_TEXTMOD_FILTER)                += sf_textmod.o
+OBJS-$(CONFIG_STRIPSTYLES_FILTER)            += sf_stripstyles.o
 
 # multimedia filters
 OBJS-$(CONFIG_ABITSCOPE_FILTER)              += avf_abitscope.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 6d7a535ee8..8f7d697408 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -524,6 +524,7 @@  extern const AVFilter ff_avf_showvolume;
 extern const AVFilter ff_avf_showwaves;
 extern const AVFilter ff_avf_showwavespic;
 extern const AVFilter ff_vaf_spectrumsynth;
+extern const AVFilter ff_sf_stripstyles;
 extern const AVFilter ff_sf_textmod;
 extern const AVFilter ff_svf_graphicsub2video;
 extern const AVFilter ff_svf_textsub2video;
diff --git a/libavfilter/sf_stripstyles.c b/libavfilter/sf_stripstyles.c
new file mode 100644
index 0000000000..de2be0b1a2
--- /dev/null
+++ b/libavfilter/sf_stripstyles.c
@@ -0,0 +1,246 @@ 
+/*
+ * Copyright (c) 2021 softworkz
+ *
+ * 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
+ * text subtitle filter which removes inline-styles from subtitles
+ */
+
+#include <libavcodec/ass.h>
+
+#include "libavutil/avassert.h"
+#include "libavutil/opt.h"
+#include "avfilter.h"
+#include "internal.h"
+#include "libavcodec/avcodec.h"
+#include "libavcodec/ass_split.h"
+
+typedef struct StripStylesContext {
+    const AVClass *class;
+    enum AVSubtitleType format;
+    int remove_animated;
+} StripStylesContext;
+
+typedef struct DialogContext {
+    const AVClass *class;
+    const StripStylesContext* ss_ctx;
+    AVBPrint buffer;
+    int drawing_scale;
+    int is_animated;
+} DialogContext;
+
+static void dialog_text_cb(void *priv, const char *text, int len)
+{
+    DialogContext *s = priv;
+    if (!s->drawing_scale && (!s->is_animated || !s->ss_ctx->remove_animated))
+        av_bprint_append_data(&s->buffer, text, len);
+}
+
+static void dialog_new_line_cb(void *priv, int forced)
+{
+    DialogContext *s = priv;
+    if (!s->drawing_scale && !s->is_animated)
+        av_bprint_append_data(&s->buffer, forced ? "\\N" : "\\n", 2);
+}
+
+static void dialog_drawing_mode_cb(void *priv, int scale)
+{
+    DialogContext *s = priv;
+    s->drawing_scale = scale;
+}
+
+static void dialog_animate_cb(void *priv, int t1, int t2, int accel, char *style)
+{
+    DialogContext *s = priv;
+    s->is_animated = 1;
+}
+
+static void dialog_move_cb(void *priv, int x1, int y1, int x2, int y2, int t1, int t2)
+{
+    DialogContext *s = priv;
+    if (t1 >= 0 || t2 >= 0)
+        s->is_animated = 1;
+}
+
+static const ASSCodesCallbacks dialog_callbacks = {
+    .text             = dialog_text_cb,
+    .new_line         = dialog_new_line_cb,
+    .drawing_mode     = dialog_drawing_mode_cb,
+    .animate          = dialog_animate_cb,
+    .move             = dialog_move_cb,
+};
+
+static int query_formats(AVFilterContext *ctx)
+{
+    AVFilterFormats *formats;
+    AVFilterLink *inlink = ctx->inputs[0];
+    AVFilterLink *outlink = ctx->outputs[0];
+    static const enum AVSubtitleType subtitle_fmts[] = { AV_SUBTITLE_FMT_ASS, AV_SUBTITLE_FMT_NONE };
+    int ret;
+
+    /* set input subtitle format */
+    formats = ff_make_format_list(subtitle_fmts);
+    if ((ret = ff_formats_ref(formats, &inlink->outcfg.formats)) < 0)
+        return ret;
+
+    /* set output video format */
+    if ((ret = ff_formats_ref(formats, &outlink->incfg.formats)) < 0)
+        return ret;
+
+    return 0;
+}
+
+static char *ass_get_line(int readorder, int layer, const char *style,
+                        const char *speaker, const char *effect, const char *text)
+{
+    return av_asprintf("%d,%d,%s,%s,0,0,0,%s,%s",
+                       readorder, layer, style ? style : "Default",
+                       speaker ? speaker : "", effect, text);
+}
+
+static char *process_dialog(StripStylesContext *s, const char *ass_line)
+{
+    ASSDialog *dialog = ff_ass_split_dialog(NULL, ass_line);
+    char *result = NULL, *text;
+    DialogContext dlg_ctx = { 0 };
+
+    if (!dialog)
+        return NULL;
+
+    dlg_ctx.ss_ctx = s;
+
+    av_bprint_init(&dlg_ctx.buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+    ff_ass_split_override_codes(&dialog_callbacks, &dlg_ctx, dialog->text);
+
+    av_bprint_finalize(&dlg_ctx.buffer, &text);
+
+    if (text && strlen(text) > 0)
+        result = ass_get_line(dialog->readorder, dialog->layer, dialog->style, dialog->name, dialog->effect, text);
+
+    av_free(text);
+    ff_ass_free_dialog(&dialog);
+
+    return result;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *src_frame)
+{
+    StripStylesContext *s = inlink->dst->priv;
+    AVFilterLink *outlink = inlink->dst->outputs[0];
+    AVSubtitle *sub;
+    int ret;
+    AVFrame *out;
+
+    outlink->format = inlink->format;
+
+    out = av_frame_alloc();
+    if (!out) {
+        av_frame_free(&src_frame);
+        return AVERROR(ENOMEM);
+    }
+
+    out->format = outlink->format;
+
+    if ((ret = av_frame_get_buffer2(out, AVMEDIA_TYPE_SUBTITLE, 0)) < 0)
+        return ret;
+
+    out->pts                    = src_frame->pts;
+    out->repeat_pict            = src_frame->repeat_pict;
+    out->pkt_dts                = src_frame->pkt_dts;
+    out->pkt_pos                = src_frame->pkt_pos;
+    out->pkt_size               = src_frame->pkt_size;
+    out->pkt_duration           = src_frame->pkt_duration;
+    out->reordered_opaque       = src_frame->reordered_opaque;
+    out->best_effort_timestamp  = src_frame->best_effort_timestamp;
+    out->flags                  = src_frame->flags;
+
+    sub = (AVSubtitle *)src_frame->data[0];
+
+    if (sub) {
+        AVSubtitle *out_sub = av_memdup(sub, sizeof(*out_sub));
+        if (!out_sub)
+            return AVERROR(ENOMEM);
+
+        out->buf[0] = av_buffer_create((uint8_t*)out_sub, sizeof(*out_sub), avsubtitle_free_ref, NULL, AV_BUFFER_FLAG_READONLY);
+        out->data[0] = (uint8_t*)out_sub;
+
+        if (sub->num_rects) {
+            out_sub->rects = av_malloc_array(sub->num_rects, sizeof(AVSubtitleRect *));
+        }
+
+        for (unsigned i = 0; i < sub->num_rects; i++) {
+
+            AVSubtitleRect *src_rect = sub->rects[i];
+            AVSubtitleRect *dst_rect = av_memdup(src_rect, sizeof(*dst_rect));
+            out_sub->rects[i] = dst_rect;
+
+            if (src_rect->ass) {
+                dst_rect->ass = process_dialog(s, src_rect->ass);
+
+                if (dst_rect->ass) {
+                    av_log(inlink->dst, AV_LOG_DEBUG, "original: %s\n", src_rect->ass);
+                    av_log(inlink->dst, AV_LOG_DEBUG, "stripped: %s\n", dst_rect->ass);
+                }
+                else
+                    dst_rect->ass = av_strdup("");
+            }
+        }
+    }
+
+    av_frame_free(&src_frame);
+    return ff_filter_frame(outlink, out);
+}
+
+#define OFFSET(x) offsetof(StripStylesContext, x)
+#define FLAGS (AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+
+static const AVOption stripstyles_options[] = {
+    { "remove_animated", "remove animated text (default: yes)",   OFFSET(remove_animated),  AV_OPT_TYPE_BOOL, {.i64 = 1 }, 0, 1, FLAGS, 0 },
+    { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(stripstyles);
+
+static const AVFilterPad inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_SUBTITLE,
+        .filter_frame = filter_frame,
+    },
+};
+
+static const AVFilterPad outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_SUBTITLE,
+    },
+};
+
+const AVFilter ff_sf_stripstyles = {
+    .name          = "stripstyles",
+    .description   = NULL_IF_CONFIG_SMALL("Strip subtitle inline styles"),
+    .query_formats = query_formats,
+    .priv_size     = sizeof(StripStylesContext),
+    .priv_class    = &stripstyles_class,
+    FILTER_INPUTS(inputs),
+    FILTER_OUTPUTS(outputs),
+};
+